home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-30 | 98.3 KB | 3,520 lines | [TEXT/KAHL] |
- /******************************************************************************
- CPEditText.cp
-
- The PEditText Class
-
- SUPERCLASS = CAbstractText
-
- ---- DESCRIPTION ----
-
- CPEditText is a class for version 1.1.x of Symantec's THINK Class
- Library that implements a simple text editing pane. It can be used as
- a direct replacement for the standard TCL CEditText class, provided
- that the word wrapping and alignment features of CEditText are not used.
- However, since CPEditText does not use the standard Macintosh TextEdit
- routines, it supports fixed-width tabs and can be used to display and
- edit more than 32k of text.
-
- ---- LIMITATIONS ----
-
- CPEditText is designed to implement an MPW-style text editor, and as
- such it supports only a single font, size and style for text and does
- not support word-wrapping or alignment. The code also does not use
- the Macintosh Script Manager routines, so it may not work properly
- with certain international versions of System software. A future
- release of this source code may address some or all of these
- limitations.
-
- ---- VERSION HISTORY ----
-
- • 1.0b1 (27 April 1992)
- - Initial public release
-
- • 1.0b2 (30 April 1992)
- - Fixed cosmetic bug in DoClick that caused insertion caret
- to not get erased when selection range changed
-
- • 1.1b1 (17 October 1992)
- - Added support for insertion buffer ("gap")
- - Added IViewTemp, IPEditTextX, and CreateEnvironment methods
-
- • 1.1b2 (24 November 1992)
- - Added CheckInsertion method to "preflight" insertions
- - Added InsertText method and modified InsertTextPtr and
- ReplaceSelection methods to bottleneck through InsertText
- - Added UseTextHandle method
- - Added CalcLineHeight and CalcTabWidth methods
- - Created CPEditTextX.h header file for constants and macros
-
- • 1.1 (10 December 1992)
- - Added typecasts to enable code to be compiled with "check
- pointer types" option on
-
- • 1.2b1 (21 May 1993)
- - Changes to compile under Symantec C++/TCL 1.1.3
- - Added HideSelection, ScrollToSelection, ScrollToOffset and
- GetChar methods
- - Now check that pane is visible in Dawdle method
- - Now scroll when extending selection via Shift-up/down arrow
- - Fixed various minor bugs
-
- • 1.2b2 (2 June 1993)
- - Modified Draw, Activate, Deactivate and HideSelection methods
- to inhibit display of caret for noneditable text
-
- • 1.2b3 (4 June 1993)
- - Removed hook instance variables and class methods and replaced
- them with virtual hook methods
-
- • 1.2b4 (28 August 1993)
- - Added DeleteText and CountRangeCRs methods
- - Changed behavior of DoArrowKey method to conform to Apple's
- Human Interface Guidelines
-
- • 1.2 (9 September 1993)
- - Fixed bugs in Activate, Deactivate and TypeKey methods
-
- ---- LICENSE AGREEMENT ----
-
- This source code was written by and is the property of Christopher R.
- Wysocki. It may be freely distributed and used, in whole or in part, in
- any software for the Macintosh, including but not limited to commercial,
- shareware, freeware or private applications. However, any software that
- uses any part or all of this source code must include a copyright notice
- indicating that part or all of this source code is used in the software.
- You are entitled to make modifications or improvements to any portion of
- this source code; you may not, however, distribute modified versions of
- this source code. Bug reports, suggestions, or general comments
- regarding this source code are encouraged; the author can be contacted at
- the electronic mail addresses listed below. This license agreement and
- the copyright notice below must not be modified in any way and must be
- included with all distributions of this source code at all times.
-
- Copyright © 1992-1993 Christopher R. Wysocki. All rights reserved.
-
- ---- ELECTRONIC MAIL ADDRESSES ----
-
- America Online: AFA ChrisW (preferred)
- CompuServe: 72010,1140
- Internet: wysocki@netcom.com (preferred)
- afachrisw@aol.com
- 72010.1140@compuserve.com
-
- ******************************************************************************/
-
- /** Includes **/
-
- #include "CPEditText.h"
- #include "CPEditTextX.h"
-
- #include <CClipboard.h>
- #include <CScrollPane.h>
- #include <CTextEnvirons.h>
- #include <Constants.h>
- #include <Commands.h>
- #include <LongCoordinates.h>
- #include <Global.h>
- #include <TBUtilities.h>
- #include <TCLUtilities.h>
-
- #if THINK_C
- #include <asm.h>
- #endif
- #include <GestaltEqu.h>
- #include <Palettes.h>
- #include <Script.h>
-
- /** Constants **/
-
- enum {
- kUnhideSelection = FALSE,
- kHideSelection = TRUE,
- kRefreshAllTextAfter = FALSE,
- kRefreshOnlyLine = TRUE
- };
-
- /** Global Variables **/
-
- extern CBureaucrat *gGopher; // First in line to get commands
- extern CClipboard *gClipboard; // Copies and Pastes data
- extern CursHandle gIBeamCursor; // I-beam cursor for text views
- extern CursHandle gWatchCursor; // Watch cursor for waiting
- extern short gClicks; // Click counter
- extern RgnHandle gUtilRgn; // Utility region
-
- /** Class Variables **/
-
- CursHandle CPEditText::cItalicIBeamCursor = NULL;
- RgnHandle CPEditText::cSaveClipRgn = NULL;
-
- /** Local Prototypes **/
-
- static long CountCRs(Ptr textP, long numChars);
- static Boolean GetGrayRGBColor(const Rect *localRect, RGBColor *grayColor, RGBColor *prevForeColor);
- static Boolean GestaltHasAttr(OSType selector, short responseBit);
-
-
- /**** C O N S T R U C T I O N / D E S T R U C T I O N M E T H O D S ****/
-
-
- /******************************************************************************
- IPEditText
-
- Initialize a PEditText object.
- ******************************************************************************/
-
- void CPEditText::IPEditText(
- CView *anEnclosure,
- CBureaucrat *aSupervisor,
- short aWidth,
- short aHeight,
- short aHEncl,
- short aVEncl,
- SizingOption aHSizing,
- SizingOption aVSizing)
- {
- CAbstractText::IAbstractText(anEnclosure, aSupervisor, aWidth, aHeight,
- aHEncl, aVEncl, aHSizing, aVSizing, kDefaultBoundsWidth);
- IPEditTextX();
- }
-
-
- /******************************************************************************
- IViewTemp {OVERRIDE}
-
- Initialize a PEditText object from a resource template.
- ******************************************************************************/
-
- void CPEditText::IViewTemp(CView *anEnclosure, CBureaucrat *aSupervisor, Ptr viewData)
- {
- inherited::IViewTemp(anEnclosure, aSupervisor, viewData);
- IPEditTextX();
- }
-
-
- /******************************************************************************
- IPEditTextX
-
- Extra initialization for a PEditText object. Called by IPEditText
- and IViewTemp.
- ******************************************************************************/
-
- void CPEditText::IPEditTextX()
- {
- UseLongCoordinates(TRUE);
- SetWholeLines(TRUE);
-
- // Initialize instance variables
- itsTextHandle = NewHandle(0);
- itsTextLength = 0;
- itsNumLines = 1;
- itsLineStarts = (LongHandle)NewHandle(sizeof(long));
- **itsLineStarts = 0;
- #if qPEUseInsertionGap
- itsGapPosition = itsGapLength = 0;
- #endif
-
- itsTextFont = GetAppFont();
- itsTextSize = GetDefFontSize();
- itsTextFace = normal;
- itsTextMode = srcOr;
- itsTabSpaces = kDefaultTabSpaces;
- itsSpacingCmd = cmdSingleSpace;
-
- itsSelStart = itsSelEnd = itsSelAnchor = 0;
- fOutlineHilite = FALSE;
- fUseItalicCaret = FALSE;
- fShowInvisibles = FALSE;
- itsClickTime = itsCaretTime = 0;
- fCaretVisible = FALSE;
- fReallyActive = FALSE;
- fUpDownArrow = FALSE;
- itsUpDownHOffset = 0;
- #if !qTCL113
- scrollHoriz = FALSE;
- #endif
-
- CreateEnvironment();
-
- // Initialize class variables
- cItalicIBeamCursor = GetCursor(rItalicIBeamCursor);
- if (cItalicIBeamCursor != NULL)
- HNoPurge((Handle)cItalicIBeamCursor);
- if (cSaveClipRgn == NULL)
- cSaveClipRgn = NewRgn();
- }
-
-
- /******************************************************************************
- CreateEnvironment
-
- Create and initialize the text environment.
- ******************************************************************************/
-
- void CPEditText::CreateEnvironment()
- {
- CTextEnvirons *textEnvirons;
- TextInfoRec textInfo;
-
- textEnvirons = new CTextEnvirons;
- textEnvirons->ITextEnvirons();
-
- textInfo.fontNumber = itsTextFont;
- textInfo.theSize = itsTextSize;
- textInfo.theStyle = itsTextFace;
- textInfo.theMode = itsTextMode;
-
- textEnvirons->SetTextInfo(&textInfo);
- itsEnvironment = textEnvirons;
-
- CalcLineHeight();
- }
-
-
- /******************************************************************************
- Dispose {OVERRIDE}
-
- Dispose of a PEditText object.
- ******************************************************************************/
-
- void CPEditText::Dispose()
- {
- ForgetHandle(itsTextHandle);
- ForgetHandle(itsLineStarts);
- inherited::Dispose();
- }
-
-
- /**** D I S P L A Y M E T H O D S ****/
-
-
- /******************************************************************************
- Draw {OVERRIDE}
-
- Draw the contents of the PEditText pane.
- ******************************************************************************/
-
- void CPEditText::Draw(Rect *area)
- {
- LongRect longArea;
- long startLine;
- long endLine;
- long vertInset = VertInset();
-
- // Compute and draw the visible text lines
- QDToFrameR(area, &longArea);
- startLine = (longArea.top - vertInset) / itsLineHeight;
- endLine = (longArea.bottom - vertInset) / itsLineHeight;
- endLine = Min(endLine, itsNumLines - 1);
- DrawLineRange(startLine, endLine, 0, kDontEraseText);
-
- // If printing is not in progress, highlight the current selection
- // or draw the insertion caret, as appropriate
- if (!this->printing && this->wantsClicks && (fReallyActive || fOutlineHilite)) {
- if (itsSelStart != itsSelEnd)
- HiliteTextRange(itsSelStart, itsSelEnd);
- else if (fCaretVisible)
- DrawCaret();
- }
- }
-
-
- /******************************************************************************
- Activate {OVERRIDE}
-
- Activate a PEditText pane by hiliting the selection or showing
- the text insertion caret.
- ******************************************************************************/
-
- void CPEditText::Activate()
- {
- Boolean wasActive = fReallyActive;
-
- // Remove the outlined selection range or erase the inactive caret
- if (!wasActive)
- HideSelection(kHideSelection, kRedraw);
-
- // Set instance variable to indicate that we're really active
- fReallyActive = TRUE;
-
- // Call the inherited method to activate the pane
- inherited::Activate();
-
- // Hilite the now active selection range, if appropriate
- if (!wasActive && this->wantsClicks && (this->editable || (itsSelStart != itsSelEnd)))
- HideSelection(kUnhideSelection, kRedraw);
- }
-
-
- /******************************************************************************
- Deactivate {OVERRIDE}
-
- Deactivate a PEditText pane by unhiliting the selection or hiding
- the text insertion caret.
- ******************************************************************************/
-
- void CPEditText::Deactivate()
- {
- Boolean wasActive = fReallyActive;
-
- // Unhilite the selection range or erase the insertion caret
- if (wasActive)
- HideSelection(kHideSelection, kRedraw);
-
- // Set instance variable to indicate that we're not really active
- fReallyActive = FALSE;
-
- // Call the inherited method to deactivate the pane
- inherited::Deactivate();
-
- // Outline the now inactive selection range, if appropriate
- if (wasActive && fOutlineHilite && this->wantsClicks && (this->editable || (itsSelStart != itsSelEnd)))
- HideSelection(kUnhideSelection, kRedraw);
- }
-
-
- /******************************************************************************
- SetSelection {OVERRIDE}
-
- Sets the selected text to the range corresponding to character
- positions selStart through selEnd.
- ******************************************************************************/
-
- void CPEditText::SetSelection(long selStart, long selEnd, Boolean redraw)
- {
- // Ensure that the positions are valid
- selStart = Max(selStart, 0);
- selStart = Min(selStart, itsTextLength);
- selEnd = Max(selEnd, 0);
- selEnd = Min(selEnd, itsTextLength);
- selEnd = Max(selEnd, selStart);
-
- // Take a quick exit if the selection range isn't being changed
- if ((selStart == itsSelStart) && (selEnd == itsSelEnd))
- return;
-
- // Unhilite the old selection range
- HideSelection(kHideSelection, redraw);
-
- // Update our instance variables
- itsSelStart = selStart;
- itsSelEnd = selEnd;
- fUpDownArrow = FALSE;
-
- // Ensure that the anchor point is one of the selection endpoints
- if ((itsSelAnchor != selStart) && (itsSelAnchor != selEnd))
- itsSelAnchor = selStart;
-
- // Hilite the new selection range
- if (this->editable || (selStart != selEnd))
- HideSelection(kUnhideSelection, redraw);
- }
-
-
- /******************************************************************************
- HideSelection {OVERRIDE}
-
- Hide/unhide the current selection and the blinking cursor.
- Does not change the active or gopher state.
- ******************************************************************************/
-
- void CPEditText::HideSelection(Boolean hide, Boolean redraw)
- {
- if (redraw) {
- Prepare();
- if (itsSelStart != itsSelEnd)
- HiliteTextRange(itsSelStart, itsSelEnd);
- else {
- if (hide)
- HideCaret();
- else
- ShowCaret();
- }
- }
- }
-
-
- /******************************************************************************
- ScrollToSelection {OVERRIDE}
-
- Scroll the text so that the current selection is visible within
- the frame. This method will scroll the text so that the selection
- is in the middle of the frame.
- ******************************************************************************/
-
- void CPEditText::ScrollToSelection()
- {
- long startLine, endLine;
- short hSpan, vSpan;
- LongPt selPos, selPt;
- long selStart, selEnd;
-
- // Calculate the number of panorama units spanned by the frame
- GetFrameSpan(&hSpan, &vSpan);
-
- // Determine the starting and ending lines for the selection
- GetSelection(&selStart, &selEnd);
- startLine = FindLine(selStart);
- endLine = (selStart == selEnd ? startLine : FindLine(selEnd));
-
- // Calculate the new vertical scroll position
- if (startLine >= position.v + vSpan) {
- selPos.v = startLine - (vSpan / 2) + 1;
- selPos.v = Max(selPos.v, 0);
- selPos.v = Min(selPos.v, itsNumLines - vSpan);
- }
- else if (endLine < position.v) {
- selPos.v = endLine - (vSpan / 2) + 1;
- selPos.v = Max(selPos.v, 0);
- selPos.v = Min(selPos.v, itsNumLines - vSpan);
- }
- else
- selPos.v = position.v;
-
- // Calculate the new horizontal scroll position
- if (!scrollHoriz)
- selPos.h = position.h;
- else {
- GetCharPoint(selPos.v == startLine ? selStart : selEnd, &selPt);
- selPos.h = (selPt.h / hScale) - (hSpan / 2);
- selPos.h = Max(0, selPos.h);
- }
-
- // Scroll the pane if necessary
- if ((selPos.v != position.v) || (selPos.h != position.h))
- ScrollTo(&selPos, kRedraw);
- }
-
-
- /******************************************************************************
- ScrollToOffset
-
- Scroll the text so that the character at the given offset is visible
- within the frame. Unlike ScrollToSelection, this method will scroll
- the text as little as is necessary to make the character visible.
- ******************************************************************************/
-
- void CPEditText::ScrollToOffset(long charOffset)
- {
- long charLine;
- short hSpan, vSpan;
- LongPt selPos, selPt;
-
- // Calculate the number of panorama units spanned by the frame
- GetFrameSpan(&hSpan, &vSpan);
-
- // Calculate the new vertical scroll position
- charLine = FindLine(charOffset);
- if (charLine >= position.v + vSpan)
- selPos.v = charLine - vSpan + 1;
- else if (charLine < position.v)
- selPos.v = charLine;
- else
- selPos.v = position.v;
-
- // Calculate the new horizontal scroll position
- if (!scrollHoriz)
- selPos.h = position.h;
- else {
- GetCharPoint(charOffset, &selPt);
- selPos.h = selPt.h / hScale;
- if (selPos.h >= position.h + hSpan)
- selPos.h -= (hSpan * 3) / 4;
- else if (selPos.h < position.h)
- selPos.h -= hSpan / 4;
- else
- selPos.h = position.h;
- selPos.h = Max(selPos.h, 0);
- }
-
- // Scroll the pane if necessary
- if ((selPos.v != position.v) || (selPos.h != position.h))
- ScrollTo(&selPos, kRedraw);
- }
-
-
- /**** C U R S O R M E T H O D S ****/
-
-
- /******************************************************************************
- AdjustCursor {OVERRIDE}
-
- Adjust the shape of the cursor according the position of the mouse.
- ******************************************************************************/
-
- void CPEditText::AdjustCursor(Point where, RgnHandle mouseRgn)
- {
- if ((itsTextFace & italic) && fUseItalicCaret && (cItalicIBeamCursor != NULL))
- SetCursor(*cItalicIBeamCursor);
- else
- SetCursor(*gIBeamCursor);
- }
-
-
- /******************************************************************************
- Dawdle {OVERRIDE}
-
- The user isn't doing anything, so flash the text insertion caret.
- Also check if the insertion gap needed to be repositioned and/or
- resized.
- ******************************************************************************/
-
- void CPEditText::Dawdle(long *maxSleep)
- {
- long ticks;
-
- if (editable && visible) {
-
- // Flash the insertion caret
- if ((itsSelStart == itsSelEnd) && ((ticks = TickCount()) >= itsCaretTime)) {
- Prepare();
- DrawCaret();
- fCaretVisible = !fCaretVisible;
- itsCaretTime = ticks + GetCaretTime();
- *maxSleep = GetCaretTime();
- }
-
- #if qPEUseInsertionGap
- // Move and resize the insertion gap
- SetGapPosition(itsSelStart);
- if ((itsGapLength < kStandardGapLength / 2) || (itsGapLength > kStandardGapLength * 2))
- SetGapLength(kStandardGapLength);
- #endif
- }
- }
-
-
- /**** M O U S E A N D K E Y S T R O K E M E T H O D S ****/
-
-
- /******************************************************************************
- DoClick {OVERRIDE}
-
- Respond to a mouse click within the PEditText. A single click
- positions the insertion point, a double click selects a word, a
- triple click selects a line or paragraph, and a quadruple click
- selects all of the text.
- ******************************************************************************/
-
- void CPEditText::DoClick(Point hitPt, short modifierKeys, long when)
- {
- LongPt framePt;
- LongPt autoScrollPt;
- long selStart, selEnd;
- long charOffset, origCharOffset, lastCharOffset;
- long newSelStart, newSelEnd;
- long vertInset = VertInset();
-
- // Get the current selection range
- GetSelection(&selStart, &selEnd);
-
- // Determine which character was clicked on
- QDToFrame(hitPt, &framePt);
- charOffset = GetCharOffset(&framePt);
- origCharOffset = lastCharOffset = charOffset;
-
- // Determine the new selection range based on the number of clicks
- if (gClicks == 1) {
- // If the Shift key is down, extend the selection
- if (modifierKeys & shiftKey)
- origCharOffset = lastCharOffset = itsSelAnchor;
- else
- SetSelection(charOffset, charOffset, kRedraw);
- }
- else if (gClicks == 2) {
- GetWordBounds(charOffset, &selStart, &selEnd);
- SetSelection(selStart, selEnd, kRedraw);
- }
- else {
- GetParagraphBounds(charOffset, &selStart, &selEnd);
-
- if ((itsSelStart != itsSelEnd) && (selStart < itsSelStart) && (selEnd > itsSelEnd)) {
- HiliteTextRange(selStart, itsSelStart);
- HiliteTextRange(itsSelEnd, selEnd);
- SetSelection(selStart, selEnd, kNoRedraw);
- }
- else if ((selStart != itsSelStart) || (selEnd != itsSelEnd))
- SetSelection(selStart, selEnd, kRedraw);
- }
-
- // Ensure that the insertion caret is visible
- if (selStart == selEnd)
- ShowCaret();
-
- // Track the selection while the mouse is down
- while (StillDown()) {
-
- // Get the current mouse position in frame coordinates
- GetMouse(&hitPt);
- QDToFrame(hitPt, &framePt);
- autoScrollPt = framePt;
- PinInRect(&frame, &framePt);
-
- // Check if the mouse has moved to a new character
- charOffset = GetCharOffset(&framePt);
- if (charOffset != lastCharOffset) {
-
- // Remember the current selection range
- selStart = itsSelStart;
- selEnd = itsSelEnd;
-
- // Determine the new selection range
- if (gClicks == 1) {
- newSelStart = Min(origCharOffset, charOffset);
- newSelEnd = Max(origCharOffset, charOffset);
- }
- else if (gClicks == 2) {
- if (charOffset < origCharOffset) {
- newSelStart = WordBreakHook(charOffset, kBreakLeft);
- newSelEnd = WordBreakHook(origCharOffset, kBreakRight);
- }
- else {
- newSelStart = WordBreakHook(origCharOffset, kBreakLeft);
- newSelEnd = WordBreakHook(charOffset, kBreakRight);
- }
- }
- else { // gClicks == 3
- if (charOffset < origCharOffset) {
- GetParagraphBounds(origCharOffset, NULL, &newSelEnd);
- GetParagraphBounds(charOffset, &newSelStart, NULL);
- }
- else {
- GetParagraphBounds(origCharOffset, &newSelStart, NULL);
- GetParagraphBounds(charOffset, NULL, &newSelEnd);
- }
- }
-
- // Adjust the hilited text
- if ((selStart == selEnd) && fCaretVisible && ((selStart != newSelStart) || (selStart != newSelEnd)))
- HideCaret();
- if (selStart != newSelStart)
- HiliteTextRange(Min(selStart, newSelStart), Max(selStart, newSelStart));
- if (selEnd != newSelEnd)
- HiliteTextRange(Min(selEnd, newSelEnd), Max(selEnd, newSelEnd));
-
- SetSelection(newSelStart, newSelEnd, kNoRedraw);
-
- if (newSelStart == newSelEnd)
- ShowCaret();
-
- // Remember the current character offset
- lastCharOffset = charOffset;
- }
-
- // Scroll the text automatically while the user is selecting text
- AutoScroll(&autoScrollPt);
- }
-
- // Determine the anchor point for the selection
- if (gClicks == 1)
- itsSelAnchor = origCharOffset;
- else if (gClicks == 2)
- itsSelAnchor = WordBreakHook(origCharOffset, charOffset < origCharOffset ? kBreakRight : kBreakLeft);
- else {
- GetParagraphBounds(origCharOffset, &selStart, &selEnd);
- itsSelAnchor = (charOffset < origCharOffset ? selEnd : selStart);
- }
-
- // For non-editable but selectable text, we want to display a selection
- // range so the user can copy, but we don't want to display a caret
- if (!this->editable && (itsSelStart == itsSelEnd))
- HideCaret();
-
- // Notify our dependents that the selection has changed
- SelectionChanged();
- }
-
-
- /******************************************************************************
- HitSamePart {OVERRIDE}
-
- Check whether two points hit the same character in the text.
- The points are in Window coordinates.
- ******************************************************************************/
-
- Boolean CPEditText::HitSamePart(Point pointA, Point pointB)
- {
- LongPt framePtA, framePtB;
-
- WindToFrame(pointA, &framePtA);
- WindToFrame(pointB, &framePtB);
-
- return (GetCharOffset(&framePtA) == GetCharOffset(&framePtB));
- }
-
-
- /******************************************************************************
- DoKeyDown {OVERRIDE}
-
- Respond to a keystroke.
- ******************************************************************************/
-
- void CPEditText::DoKeyDown(char theChar, Byte keyCode, EventRecord *macEvent)
- {
- if (IsArrowKey(theChar) && (macEvent->modifiers & cmdKey)) {
- DoArrowKey(theChar, macEvent->modifiers);
-
- // Notify our dependents that the selection has changed
- // This used to be done in DoArrowKey, but we now do it here
- // so the CTextEditTask::DoFwdDelete works properly
- SelectionChanged();
- }
- else
- inherited::DoKeyDown(theChar, keyCode, macEvent);
- }
-
-
- /******************************************************************************
- TypeChar {OVERRIDE}
-
- Process a single keystroke. All undo setup has already been
- performed, and the keystroke should be handled directly.
- ******************************************************************************/
-
- void CPEditText::TypeChar(char theChar, short theModifiers)
- {
- // Need to check for arrow keys here, even though we check in the
- // DoKeyDown method, since CTextEditTask calls TypeChar directly
- if (IsArrowKey(theChar))
- DoArrowKey(theChar, theModifiers);
- else {
- long selStart = itsSelStart;
- long selEnd = itsSelEnd;
-
- // Obscure the cursor so it is hidden until the mouse is moved
- ObscureCursor();
-
- // Now perform the typing
- if (theChar == kBackspace) {
- if (selStart == selEnd && selStart > 0) {
- HideCaret();
- DeleteTextRange(selStart - 1, selStart, kRedraw);
- ShowCaret();
- }
- else
- DeleteTextRange(selStart, selEnd, kRedraw);
- }
- else {
- if (selStart != selEnd)
- ReplaceSelection(&theChar, sizeof(char));
- else
- InsertTextPtr(&theChar, sizeof(char), kNoRedraw);
- }
-
- // Ensure that the insertion point is visible
- ScrollToOffset(itsSelStart);
- }
- }
-
-
- /******************************************************************************
- DoArrowKey
-
- Handle a cursor key. The behavior is as follows:
-
- modifier key
- <none> Option Command
-
- up prev line prev page first line
- down next line next page last line
- left prev char prev word line start
- right next char next word line end
-
- ******************************************************************************/
-
- void CPEditText::DoArrowKey(char theChar, short theModifiers)
- {
- Boolean commandKeyDown = ((theModifiers & cmdKey) != 0);
- Boolean optionKeyDown = ((theModifiers & optionKey) != 0);
- Boolean shiftKeyDown = ((theModifiers & shiftKey) != 0);
- Boolean isInsertion;
- Boolean isUpDownArrow;
- register long nonAnchor;
- register long selChar;
- long numLines;
- long selLine;
- long selStart;
- long selEnd;
- long textLength;
- long newStart;
- long newEnd;
- LongPt charPt;
-
- GetSelection(&selStart, &selEnd);
- textLength = GetLength();
- numLines = GetNumLines();
-
- // Determine if we have an insertion point or not
- isInsertion = (selStart == selEnd);
-
- // Reset the selection anchor position if necessary
- if (isInsertion)
- itsSelAnchor = selStart;
- else if (itsSelAnchor < 0) {
- if ((theChar == kLeftCursor) || (theChar == kUpCursor))
- itsSelAnchor = selEnd;
- else
- itsSelAnchor = selStart;
- }
-
- // Determine the non-anchored selection position
- nonAnchor = (itsSelAnchor == selEnd ? selStart : selEnd);
-
- // Now handle the keystroke
- if ((theChar == kLeftCursor) || (theChar == kRightCursor)) {
- if (commandKeyDown) {
- selLine = FindLine(nonAnchor);
-
- if (theChar == kLeftCursor)
- selChar = GetLineStart(selLine);
- else {
- selChar = GetLineEnd(selLine);
- if (selLine < numLines - 1)
- --selChar;
- }
- }
- else if (optionKeyDown) {
- Boolean expanding;
-
- if (shiftKeyDown) {
- if (theChar == kLeftCursor)
- expanding = (nonAnchor <= itsSelAnchor);
- else
- expanding = (nonAnchor >= itsSelAnchor);
- }
- else
- expanding = TRUE;
-
- selChar = nonAnchor;
- if (!expanding) {
- if (theChar == kLeftCursor) {
- newEnd = selChar;
- while ((selChar > itsSelAnchor) && ((!GetWordBounds(selChar, &newStart, &newEnd)) || (newEnd + 1 >= nonAnchor)))
- selChar = newStart - 1;
- if (selChar <= itsSelAnchor) {
- expanding = TRUE;
- selChar = itsSelAnchor;
- }
- else
- selChar = newEnd;
- }
- else {
- newStart = selChar;
- while ((selChar < itsSelAnchor) && ((!GetWordBounds(selChar, &newStart, &newEnd)) || (newStart <= nonAnchor)))
- selChar = newEnd + 1;
- if (selChar >= itsSelAnchor) {
- expanding = TRUE;
- selChar = itsSelAnchor;
- }
- }
- }
-
- if (expanding) {
- if (theChar == kLeftCursor) {
- long minSelChar = selChar;
-
- newStart = selChar;
- while ((selChar > 0) && ((!GetWordBounds(selChar, &newStart, &newEnd)) || (newStart >= minSelChar)))
- --selChar;
- selChar = Min(selChar, newStart);
- }
- else {
- long maxSelChar = textLength - 1;
- newEnd = selChar;
- while ((selChar < maxSelChar) && !GetWordBounds(selChar + 1, &newStart, &newEnd))
- ++selChar;
- selChar = Max(selChar, newEnd);
- }
- }
- }
- else if (shiftKeyDown || isInsertion) {
- if (theChar == kLeftCursor)
- selChar = nonAnchor - 1;
- else
- selChar = nonAnchor + 1;
- selChar = Min(Max(selChar, 0), textLength);
- }
- else
- selChar = ((theChar == kLeftCursor) ? selStart : selEnd);
-
- isUpDownArrow = FALSE;
- }
- else { // ((theChar == kUpCursor) || (theChar == kDownCursor))
-
- selLine = FindLine(nonAnchor);
-
- if ((theChar == kUpCursor) && (commandKeyDown || (selLine == 0))) {
- selChar = 0;
- isUpDownArrow = FALSE;
- }
- else if ((theChar == kDownCursor) && (commandKeyDown || (selLine == numLines - 1))) {
- selChar = textLength;
- isUpDownArrow = FALSE;
- }
- else {
- if (!fUpDownArrow) {
- GetCharPoint(nonAnchor, &charPt);
- itsUpDownHOffset = charPt.h;
- }
-
- if (optionKeyDown) {
- short hSpan, vSpan;
-
- GetFrameSpan(&hSpan, &vSpan);
- if (theChar == kUpCursor)
- selLine -= vSpan;
- else
- selLine += vSpan;
- }
- else {
- if (theChar == kUpCursor)
- --selLine;
- else
- ++selLine;
- }
-
- GetCharPoint(GetLineStart(selLine), &charPt);
- charPt.h = (long)itsUpDownHOffset;
- selChar = GetCharOffset(&charPt);
- isUpDownArrow = TRUE;
- }
- }
-
- // Set the new selection, extending it if the Shift key was down
- if (!shiftKeyDown)
- SetSelection(selChar, selChar, kRedraw);
- else {
- newStart = Min(selChar, itsSelAnchor);
- newEnd = Max(selChar, itsSelAnchor);
-
- HideCaret();
-
- if ((selStart == selEnd) || (newStart == newEnd))
- SetSelection(newStart, newEnd, kRedraw);
- else {
- if (newStart != selStart)
- HiliteTextRange(Min(newStart, selStart), Max(newStart, selStart));
- if (newEnd != selEnd)
- HiliteTextRange(Min(newEnd, selEnd), Max(newEnd, selEnd));
- SetSelection(newStart, newEnd, kNoRedraw);
- }
- }
-
- // Make sure the selected character is visible within the frame
- ScrollToOffset(selChar);
-
- // Remember if this character was an up or down arrow
- fUpDownArrow = isUpDownArrow;
- }
-
-
- /**** T E X T S P E C I F I C A T I O N M E T H O D S ****/
-
-
- /******************************************************************************
- SetTextPtr {OVERRIDE}
-
- Replace all the text with the text in the given buffer.
- ******************************************************************************/
-
- void CPEditText::SetTextPtr(Ptr textPtr, long numChars)
- {
- long prevSelStart = itsSelStart;
- long prevSelEnd = itsSelEnd;
-
- // Check that enough memory is available for the operation to succeed
- TRY {
- SetSelection(0, itsTextLength, kNoRedraw);
- CheckInsertion(textPtr, numChars, kUseSelection, NULL, NULL);
- }
- CATCH {
- SetSelection(prevSelStart, prevSelEnd, kNoRedraw);
- }
- ENDTRY
-
- // Resize the text handle and copy the contents of the given buffer
- // into it, then recalculate the line starts and bounds rectangle
- #if qPEUseInsertionGap
- CloseGap();
- #endif
- SetHandleSize(itsTextHandle, numChars);
- FailMemError();
- BlockMove(textPtr, *itsTextHandle, numChars);
- itsTextLength = numChars;
- itsSelStart = itsSelEnd = 0;
-
- CalcLineStarts();
- Refresh();
- }
-
-
- /******************************************************************************
- UseTextHandle
-
- Replace all the text with the contents of a given handle. Unlike
- SetTextHandle, this method does not copy the contents of the handle,
- so the caller must not dispose of the handle.
- ******************************************************************************/
-
- void CPEditText::UseTextHandle(Handle textHandle)
- {
- ASSERT(textHandle != NULL);
- if (textHandle != itsTextHandle) {
- ForgetHandle(itsTextHandle);
- itsTextHandle = textHandle;
- }
- itsTextLength = GetHandleSize(textHandle);
- itsSelStart = itsSelEnd = 0;
- #if qPEUseInsertionGap
- itsGapPosition = itsGapLength = 0;
- #endif
-
- CalcLineStarts();
- Refresh();
- }
-
-
- /******************************************************************************
- InsertTextPtr {OVERRIDE}
-
- Insert a copy of the given text at the start of the selection.
- ******************************************************************************/
-
- void CPEditText::InsertTextPtr(Ptr insertPtr, long insertLen, Boolean redraw)
- {
- long numInsertCRs;
-
- // Check that enough memory is available for the insertion to succeed
- // As a side effect, CheckInsertion also counts the number of carriage
- // returns in the text being inserted
- CheckInsertion(insertPtr, insertLen, kDontUseSelection, &numInsertCRs, NULL);
-
- // Call the internal method InsertText to insert the text
- InsertText(insertPtr, insertLen, numInsertCRs, redraw);
- }
-
-
- /******************************************************************************
- CopyTextRange {OVERRIDE}
-
- Return a handle to a copy of the given range of text.
- ******************************************************************************/
-
- Handle CPEditText::CopyTextRange(long start, long end)
- {
- Handle copyHandle;
- long length;
-
- // Determine the length of text to copy
- end = Min(end, itsTextLength);
- length = Max(end - start, 0);
-
- // Allocate a handle for the copied text
- copyHandle = NewHandleCanFail(length);
- FailNIL(copyHandle);
-
- // Copy the text from our buffer to the handle just allocated
- if (length > 0) {
- #if qPEUseInsertionGap
- Ptr textP = *itsTextHandle;
- long gapPosition = itsGapPosition;
- long gapLength = itsGapLength;
-
- if (end <= gapPosition)
- BlockMove(textP + start, *copyHandle, length);
- else if (start >= gapPosition)
- BlockMove(textP + start + gapLength, *copyHandle, length);
- else {
- long lengthBeforeGap = gapPosition - start;
- BlockMove(textP + start, *copyHandle, lengthBeforeGap);
- BlockMove(textP + start + gapLength + lengthBeforeGap, *copyHandle + lengthBeforeGap, length - lengthBeforeGap);
- }
- #else
- BlockMove(*itsTextHandle + start, *copyHandle, length);
- #endif
- }
-
- return copyHandle;
- }
-
-
- /******************************************************************************
- DeleteTextRange
-
- Delete the given range of text.
- ******************************************************************************/
-
- void CPEditText::DeleteTextRange(long start, long end, Boolean redraw)
- {
- DeleteText(start, end, CountRangeCRs(start, end), redraw);
- }
-
-
- /******************************************************************************
- ReplaceTextRange
-
- Replace a range of characters with the given text.
- ******************************************************************************/
-
- void CPEditText::ReplaceTextRange(long start, long end, Ptr replacePtr, long replaceLen)
- {
- HideSelection(kHideSelection, kRedraw);
- SetSelection(start, end, kNoRedraw);
- ReplaceSelection(replacePtr, replaceLen);
- }
-
-
- /******************************************************************************
- ReplaceSelection
-
- Replace the current selection with the given text. If replacePtr
- is NULL, just delete the current selection.
- ******************************************************************************/
-
- void CPEditText::ReplaceSelection(Ptr replacePtr, long replaceLen)
- {
- Boolean refresh;
- long selStart;
- long selEnd;
- long numReplaceCRs;
- long numInsertCRs;
- long numLinesDelta;
-
- // Check that enough memory is available for the replacement to succeed
- // As a side effect, CheckInsertion also counts the number of carriage
- // returns in the text being inserted and in the current selection
- CheckInsertion(replacePtr, replaceLen, kUseSelection, &numInsertCRs, &numReplaceCRs);
-
- // Delete the current selection, if any
- GetSelection(&selStart, &selEnd);
- if (selStart < selEnd) {
- DeleteText(selStart, selEnd, numReplaceCRs, kNoRedraw);
- numLinesDelta = -numReplaceCRs;
- refresh = TRUE;
- }
- else {
- numLinesDelta = 0;
- refresh = FALSE;
- }
-
- // Insert the new text, if any
- if ((replacePtr != NULL) && (replaceLen > 0)) {
- InsertText(replacePtr, replaceLen, numInsertCRs, kNoRedraw);
- SetSelection(selStart + replaceLen, selStart + replaceLen, kNoRedraw);
- refresh = (numLinesDelta != 0);
- }
-
- // Refresh the text if necessary
- if (refresh)
- RefreshTextAfter(selStart, (numLinesDelta == 0));
- }
-
-
- /******************************************************************************
- PerformEditCommand {OVERRIDE}
-
- Perform the standard cut, copy, paste, and clear commands on the text.
- ******************************************************************************/
-
- void CPEditText::PerformEditCommand(long theCommand)
- {
- Handle textH;
- long selStart;
- long selEnd;
-
- Prepare();
- GetSelection(&selStart, &selEnd);
-
- // Copy the selection range to the Clipboard for Cut or Copy
- if (((theCommand == cmdCut) || (theCommand == cmdCopy)) && (selStart != selEnd)) {
- textH = CopyTextRange(selStart, selEnd);
- TRY {
- gClipboard->EmptyScrap();
- gClipboard->PutData('TEXT', textH);
- DisposHandle(textH);
- }
- CATCH {
- ForgetHandle(textH);
- }
- ENDTRY
- }
-
- // Delete the selection range for Cut or Clear
- if (((theCommand == cmdCut) || (theCommand == cmdClear)) && (selStart < selEnd))
- DeleteTextRange(selStart, selEnd, kRedraw);
-
- // Replace the current selection with the contents of the Clipboard for Paste
- else if ((theCommand == cmdPaste) && gClipboard->GetData('TEXT', &textH)) {
- HLockHi(textH);
- TRY {
- ReplaceSelection(*textH, GetHandleSize(textH));
- DisposHandle(textH);
- }
- CATCH {
- ForgetHandle(textH);
- }
- ENDTRY
- }
-
- // Ensure that the insertion caret is visible
- ScrollToOffset(itsSelStart);
- }
-
-
- /**** T E X T C H A R A C T E R I S T I C S M E T H O D S ****/
-
-
- /******************************************************************************
- SetFontNumber {OVERRIDE}
-
- Specify the font for text by font number.
- ******************************************************************************/
-
- void CPEditText::SetFontNumber(short aFontNumber)
- {
- TextInfoRec textInfo;
-
- itsTextFont = aFontNumber;
-
- ((CTextEnvirons *)itsEnvironment)->GetTextInfo(&textInfo);
- textInfo.fontNumber = aFontNumber;
- ((CTextEnvirons *)itsEnvironment)->SetTextInfo(&textInfo);
-
- CalcLineHeight();
- }
-
-
- /******************************************************************************
- SetFontSize {OVERRIDE}
-
- Specify the point size of text.
- ******************************************************************************/
-
- void CPEditText::SetFontSize(short aSize)
- {
- TextInfoRec textInfo;
-
- itsTextSize = aSize;
-
- ((CTextEnvirons *)itsEnvironment)->GetTextInfo(&textInfo);
- textInfo.theSize = aSize;
- ((CTextEnvirons *)itsEnvironment)->SetTextInfo(&textInfo);
-
- CalcLineHeight();
- }
-
-
- /******************************************************************************
- SetFontStyle {OVERRIDE}
-
- Specify the style, such as bold or italic, for text.
- ******************************************************************************/
-
- void CPEditText::SetFontStyle(short aStyle)
- {
- TextInfoRec textInfo;
-
- if (aStyle == normal) // Plain text is the absence of any style
- itsTextFace = normal;
- else
- itsTextFace ^= aStyle; // Toggle style characteristic by XOR'ing proper bit
-
- ((CTextEnvirons *)itsEnvironment)->GetTextInfo(&textInfo);
- textInfo.theStyle = itsTextFace;
- ((CTextEnvirons *)itsEnvironment)->SetTextInfo(&textInfo);
-
- CalcLineHeight();
- }
-
-
- /******************************************************************************
- SetTextMode {OVERRIDE}
-
- Specify the transfer mode used for drawing text.
- ******************************************************************************/
-
- void CPEditText::SetTextMode(short aMode)
- {
- TextInfoRec textInfo;
-
- itsTextMode = aMode;
-
- ((CTextEnvirons *)itsEnvironment)->GetTextInfo(&textInfo);
- textInfo.theMode = aMode;
- ((CTextEnvirons *)itsEnvironment)->SetTextInfo(&textInfo);
-
- Refresh();
- }
-
-
- /******************************************************************************
- SetAlignCmd {OVERRIDE}
-
- Specify the alignment for text.
- ******************************************************************************/
-
- void CPEditText::SetAlignCmd(long anAlignCmd)
- {
- // Null method -- alignment not supported by CPEditText
- }
-
-
- /******************************************************************************
- GetAlignCmd {OVERRIDE}
-
- Return the current alignment.
- ******************************************************************************/
-
- long CPEditText::GetAlignCmd()
- {
- return cmdAlignLeft;
- }
-
-
- /******************************************************************************
- SetSpacingCmd {OVERRIDE}
-
- Specify the vertical spacing between lines of text.
- ******************************************************************************/
-
- void CPEditText::SetSpacingCmd(long aSpacingCmd)
- {
- itsSpacingCmd = aSpacingCmd;
- CalcLineHeight();
- }
-
-
- /******************************************************************************
- GetSpacingCmd {OVERRIDE}
-
- Return the vertical spacing between lines of text.
- ******************************************************************************/
-
- long CPEditText::GetSpacingCmd()
- {
- return itsSpacingCmd;
- }
-
- #if !qTCL113
-
- /******************************************************************************
- SetHorizontalScroll
-
- Enable/disable automatic horizontal scrolling. If TRUE, typing
- and editing can cause automatic horizontal scrolling to keep the
- selection range or insertion point in view. (Automatic vertical
- scrolling is always enabled.) If FALSE, the insertion point is
- allowed to travel out of sight.
-
- (This method is provided in CAbstractText with TCL 1.1.3, so
- it is only necessary when compiling for TCL 1.1.2 or earlier.)
- ******************************************************************************/
-
- void CPEditText::SetHorizontalScroll(Boolean doHoriz)
- {
- scrollHoriz = doHoriz;
- }
-
- #endif // !qTCL113
-
- /******************************************************************************
- SetTabSpaces
-
- Set the number of spaces per tab.
- ******************************************************************************/
-
- void CPEditText::SetTabSpaces(short tabSpaces)
- {
- itsTabSpaces = Max(tabSpaces, 1);
- CalcTabWidth();
- Refresh();
- }
-
-
- /******************************************************************************
- GetTabSpaces
-
- Return the number of spaces per tab.
- ******************************************************************************/
-
- short CPEditText::GetTabSpaces()
- {
- return itsTabSpaces;
- }
-
-
- /******************************************************************************
- SetOutlineHiliting
-
- Set whether or not to outline an inactive selection range and
- to draw the inactive insertion caret in gray.
- ******************************************************************************/
-
- void CPEditText::SetOutlineHiliting(Boolean outlineHilite)
- {
- fOutlineHilite = outlineHilite;
- if (!fReallyActive)
- Refresh();
- }
-
-
- /******************************************************************************
- SetItalicCaret
-
- Set whether or not to use a slanted cursor for italic text.
- ******************************************************************************/
-
- void CPEditText::SetItalicCaret(Boolean useItalicCaret)
- {
- HideSelection(kHideSelection, kRedraw);
- fUseItalicCaret = useItalicCaret;
- HideSelection(kUnhideSelection, kRedraw);
- }
-
-
- /******************************************************************************
- SetShowInvisibles
-
- Set whether or not to show invisible characters.
- ******************************************************************************/
-
- void CPEditText::SetShowInvisibles(Boolean showInvisibles)
- {
- fShowInvisibles = showInvisibles;
- Refresh();
- }
-
-
- /******************************************************************************
- GetShowInvisibles
-
- Return whether or not invisible characters are shown.
- ******************************************************************************/
-
- Boolean CPEditText::GetShowInvisibles()
- {
- return fShowInvisibles;
- }
-
-
- /******************************************************************************
- GetHeight {OVERRIDE}
-
- Return the height of the indicated lines of text.
- ******************************************************************************/
-
- long CPEditText::GetHeight(long startLine, long endLine)
- {
- return (itsLineHeight * (endLine - startLine + 1));
- }
-
-
- /******************************************************************************
- GetCharOffset {OVERRIDE}
-
- Return the offset into the text buffer of the character position
- at a point in Frame coordinates. The offset does not reflect
- the position or length of the insertion gap.
- ******************************************************************************/
-
- long CPEditText::GetCharOffset(LongPt *aPt)
- {
- long line;
- register short offset;
- register short lineLen;
- register short width;
- register short lastWidth;
- register short horiz;
- register short *widthsP;
- ShortHandle widthsH;
-
- Prepare();
-
- // Determine which line the point lies on
- line = (aPt->v - VertInset()) / itsLineHeight;
-
- // Check for and handle special cases above the first line and below the last line
- if (line < 0)
- return 0;
- else if (line > itsNumLines - 1)
- return itsTextLength;
-
- // Get the number of characters in the line
- lineLen = GetLineLength(line);
-
- // Check if the horizontal coordinate is smaller than the horizontal inset of the pane
- horiz = aPt->h - HorizInset();
-
- if (horiz <= 0)
- offset = 0;
- else {
-
- // Calculate character pixel offsets for the given line
- widthsH = MeasureLineWidths(line);
- widthsP = *widthsH;
-
- offset = 0;
- lastWidth = 0;
-
- while ((offset < lineLen) && ((width = *++widthsP) < horiz)) {
- lastWidth = width;
- ++offset;
- }
-
- DisposHandle((Handle)widthsH);
-
- // Bump the offset if the point is on the right side of a character
- if (horiz >= (width + lastWidth) / 2)
- ++offset;
-
- // Set the offset to the last character on the line if the point
- // is greater than the pixel length of the line
- if (offset > lineLen || ((offset == lineLen) && (GetChar(offset) == kReturn)))
- offset = (line < itsNumLines - 1 ? lineLen - 1 : lineLen);
- }
-
- return ((*itsLineStarts)[line] + offset);
- }
-
-
- /******************************************************************************
- GetCharPoint {OVERRIDE}
-
- Return the Frame coordinates of the character at the given offset
- in the text buffer. The offset should not take into account the
- position or length of the insertion gap.
- ******************************************************************************/
-
- void CPEditText::GetCharPoint(long charOffset, LongPt *aPt)
- {
- long line;
- ShortHandle widthsH;
-
- // Ensure that the offset is within bounds
- charOffset = Max(charOffset, 0);
- charOffset = Min(charOffset, itsTextLength);
-
- // Determine which line the given character offset is on
- // and calculate the pixel widths for that line
- line = FindLine(charOffset);
- widthsH = MeasureLineWidths(line);
-
- // Compute the pixel coordinates for the character
- aPt->v = VertInset() + line * itsLineHeight + itsFontAscent;
- aPt->h = HorizInset() + (*widthsH)[charOffset - (*itsLineStarts)[line]];
-
- // Dispose of the pixel widths handle
- DisposHandle((Handle)widthsH);
- }
-
-
- /******************************************************************************
- GetTextStyle {OVERRIDE}
-
- Return current style information. whichAttributes is a set of
- flags indicating which text attributes are requested, and upon
- return, indicates which of the requested attributes are valid
- in the TextStyle record.
- ******************************************************************************/
-
- void CPEditText::GetTextStyle(short *whichAttributes, TextStyle *aStyle)
- {
- if (*whichAttributes & doFont)
- aStyle->tsFont = itsTextFont;
- if (*whichAttributes & doFace)
- aStyle->tsFace = itsTextFace;
- if (*whichAttributes & doSize)
- aStyle->tsSize = itsTextSize;
- *whichAttributes &= ~doColor;
- }
-
-
- /******************************************************************************
- GetCharStyle {OVERRIDE}
-
- Return style information about the character at the given offset.
- Since a PEditText pane only supports a single font/size/style
- for the text, this method returns information about the global
- text characteristics in the TextStyle record.
- ******************************************************************************/
-
- void CPEditText::GetCharStyle(long charOffset, TextStyle *aStyle)
- {
- aStyle->tsFont = itsTextFont;
- aStyle->tsFace = itsTextFace;
- aStyle->tsSize = itsTextSize;
- aStyle->tsColor.red = aStyle->tsColor.green = aStyle->tsColor.blue = 0;
- }
-
-
- /**** A C C E S S I N G M E T H O D S ****/
-
-
- /******************************************************************************
- SetBounds {OVERRIDE}
-
- Set the bounds of the PEditText panorama. The text is automatically
- scrolled if necessary to fill the entire frame.
- ******************************************************************************/
-
- void CPEditText::SetBounds(LongRect *aBounds)
- {
- long hDelta;
- long vDelta;
-
- // Scroll the text if necessary to fill the entire frame
- if (aBounds->right * hScale < frame.right)
- hDelta = Min(frame.left - aBounds->left * hScale, frame.right - aBounds->right * hScale);
- else
- hDelta = 0;
-
- if (aBounds->bottom * vScale < frame.bottom)
- vDelta = Min(frame.top - aBounds->top * vScale, frame.bottom - aBounds->bottom * vScale);
- else
- vDelta = 0;
-
- if ((hDelta != 0) || (vDelta != 0))
- Scroll(-hDelta / hScale, -vDelta / vScale, kRedraw);
-
- // Call the inherited method to update the bounds instance variable
- // and adjust the maximum values of the scroll bars
- inherited::SetBounds(aBounds);
- }
-
-
- #if qTCL113
-
- /******************************************************************************
- GetSteps {OVERRIDE}
-
- Return the horizontal and vertical number of units to scroll
- in a single step.
- ******************************************************************************/
-
- void CPEditText::GetSteps(short *hStep, short *vStep)
- {
- inherited::GetSteps(hStep, vStep);
-
- if ((*hStep == 1) && (hScale == 1))
- *hStep = itsMaxCharWidth;
- }
-
- #endif // qTCL113
-
- /******************************************************************************
- GetTextHandle {OVERRIDE}
-
- Return a handle to the text buffer. This method closes the
- insertion gap before returning the text handle, which can have
- a negative impact on performance. Clients who frequently call
- this method should considering calling GetRawTextHandle instead,
- although doing so will require taking into account the position
- and length of the gap.
- ******************************************************************************/
-
- Handle CPEditText::GetTextHandle()
- {
- #if qPEUseInsertionGap
- CloseGap();
- #endif
- return itsTextHandle;
- }
-
- #if qPEUseInsertionGap
-
- /******************************************************************************
- GetRawTextHandle
-
- Return a handle to the text buffer. Clients should generally
- use GetTextHandle instead of GetRawTextHandle, unless they are
- willing to contend with the insertion gap.
- ******************************************************************************/
-
- Handle CPEditText::GetRawTextHandle()
- {
- return itsTextHandle;
- }
-
- #endif // qPEUseInsertionGap
-
- /******************************************************************************
- FindLine {OVERRIDE}
-
- Return the line number containing the specified character position.
- Both lines and character positions are numbered starting from zero.
- If the character position is before the start of the text (i.e., a
- negative number), a value of zero is returned; if it is beyond the
- end of the text, the number of the last line is returned.
- ******************************************************************************/
-
- long CPEditText::FindLine(register long charPos)
- {
- register long line, lineLow, lineHigh;
- register long lineStart;
- register long *lineStarts = *itsLineStarts;
- long numLines = itsNumLines;
-
- // Check if it's in the first line
- if ((numLines == 1) || (charPos < lineStarts[1]))
- return 0;
-
- // Check if it's in the last line
- if (charPos >= lineStarts[numLines - 1])
- return numLines - 1;
-
- // Perform a binary search through itsLineStarts
- lineLow = 0;
- lineHigh = numLines;
-
- while (lineHigh >= lineLow) {
- line = (lineLow + lineHigh) >> 1;
- lineStart = lineStarts[line];
- if (charPos == lineStart)
- break;
- else if (charPos > lineStart)
- lineLow = line + 1;
- else
- lineHigh = line - 1;
- }
-
- return ((charPos < lineStart) ? line - 1 : line);
- }
-
-
- /******************************************************************************
- GetLineStart
-
- Return the offset of the character at the start of the given line.
- The line number parameter is zero-based. The offset does not
- reflect the position or length of the insertion gap.
- ******************************************************************************/
-
- long CPEditText::GetLineStart(long line)
- {
- if (line <= 0)
- return 0;
- else if (line >= itsNumLines)
- return itsTextLength;
- else
- return (*itsLineStarts)[line];
- }
-
-
- /******************************************************************************
- GetLineEnd
-
- Return the offset of the character at the end of the given line.
- The line number parameter is zero-based. The offset does not
- reflect the position or length of the insertion gap.
- ******************************************************************************/
-
- long CPEditText::GetLineEnd(long line)
- {
- if (line < 0)
- return 0;
- else if (line >= itsNumLines - 1)
- return itsTextLength;
- else
- return (*itsLineStarts)[line + 1];
- }
-
-
- /******************************************************************************
- GetLineLength
-
- Return the number of characters in the given line. The length does
- not reflect the length of the insertion gap.
- ******************************************************************************/
-
- short CPEditText::GetLineLength(long line)
- {
- long lineStart;
- long lineEnd;
- short lineLength;
-
- if ((line < 0) || (line >= itsNumLines))
- lineLength = 0;
- else {
- lineStart = (*itsLineStarts)[line];
- lineEnd = (line < itsNumLines - 1 ? (*itsLineStarts)[line + 1] : itsTextLength);
- lineLength = lineEnd - lineStart;
- }
-
- return lineLength;
- }
-
-
- /******************************************************************************
- GetLength {OVERRIDE}
-
- Return the number of characters in the text buffer. The length does
- not reflect the length of the insertion gap.
- ******************************************************************************/
-
- long CPEditText::GetLength()
- {
- return itsTextLength;
- }
-
-
- /******************************************************************************
- GetNumLines {OVERRIDE}
-
- Return the number of lines of text.
- ******************************************************************************/
-
- long CPEditText::GetNumLines()
- {
- return itsNumLines;
- }
-
-
- /******************************************************************************
- GetSelection {OVERRIDE}
-
- Return the start and end of the selection. The positions do not
- reflect the position or length of the insertion gap.
- ******************************************************************************/
-
- void CPEditText::GetSelection(long *selStart, long *selEnd)
- {
- *selStart = itsSelStart;
- *selEnd = itsSelEnd;
- }
-
-
- /******************************************************************************
- GetChar
-
- Return the character at the given position.
- ******************************************************************************/
-
- short CPEditText::GetChar(long aPosition)
- {
- #if qPEUseInsertionGap
- Ptr textP = *itsTextHandle;
-
- if (aPosition >= itsGapPosition)
- textP += itsGapLength;
-
- return (unsigned char)textP[aPosition];
- #else
- return (unsigned char)(*itsTextHandle)[aPosition];
- #endif
- }
-
-
- /******************************************************************************
- GetCharBefore {OVERRIDE}
-
- Return the character before aPosition. The character and its size
- are returned in charBuf, and aPosition is updated with the starting
- position of the character. If there is no preceding character, then
- Length(charBuf) is 0, and aPosition is not updated.
- ******************************************************************************/
-
- void CPEditText::GetCharBefore(long *aPosition, tCharBuf charBuf)
- {
- long charPos = *aPosition;
-
- if ((charPos > 0) && (charPos <= itsTextLength)) {
- charPos--;
- Length(charBuf) = 1;
- #if qPEUseInsertionGap
- charBuf[1] = (*itsTextHandle)[charPos < itsGapPosition ? charPos : charPos + itsGapLength];
- #else
- charBuf[1] = (*itsTextHandle)[charPos];
- #endif
- *aPosition = charPos;
- }
- else
- Length(charBuf) = 0;
- }
-
-
- /******************************************************************************
- GetCharAfter {OVERRIDE}
-
- Return the character after aPosition. The character and its size
- are returned in charBuf, and aPosition is updated with the starting
- position of the character. If there is no following character, then
- Length(charBuf) is 0, and aPosition is not updated.
- ******************************************************************************/
-
- void CPEditText::GetCharAfter(long *aPosition, tCharBuf charBuf)
- {
- long charPos = *aPosition;
-
- if ((charPos >= 0) && (charPos < itsTextLength)) {
- Length(charBuf) = 1;
- #if qPEUseInsertionGap
- charBuf[1] = (*itsTextHandle)[charPos < itsGapPosition ? charPos : charPos + itsGapLength];
- #else
- charBuf[1] = (*itsTextHandle)[charPos];
- #endif
- }
- else
- Length(charBuf) = 0;
- }
-
-
- /******************************************************************************
- GetWordBounds
-
- Find the starting and ending offsets of the word containing
- the given character offset. Returns TRUE if this character is part
- of a word, FALSE otherwise.
- ******************************************************************************/
-
- Boolean CPEditText::GetWordBounds(long charPos, long *wordStart, long *wordEnd)
- {
- ASSERT(wordStart != NULL);
- ASSERT(wordEnd != NULL);
-
- if ((charPos < 0) || (charPos >= itsTextLength))
- return FALSE;
- else {
- *wordStart = WordBreakHook(charPos, kBreakLeft);
- *wordEnd = WordBreakHook(charPos, kBreakRight);
- return (*wordStart < *wordEnd);
- }
- }
-
-
- /******************************************************************************
- GetParagraphBounds
-
- Find the starting and ending offsets of the paragraph containing
- the given character offset. Paragraphs are defined as a sequence
- of characters delineated by carriage returns or the beginning/end
- of the text.
- ******************************************************************************/
-
- void CPEditText::GetParagraphBounds(long charPos, long *paraStart, long *paraEnd)
- {
- if (paraStart != NULL) {
- register long start = charPos;
- while (--start >= 0 && GetChar(start) != kReturn)
- ;
- *paraStart = ++start;
- }
-
- if (paraEnd != NULL) {
- register long end = charPos;
- while (end < itsTextLength && GetChar(end++) != kReturn)
- ;
- *paraEnd = end;
- }
- }
-
-
- #if qPEUseInsertionGap
-
- /******************************************************************************
- GetGapPosition
-
- Return the position of the insertion gap. Clients will generally
- not need to use this method, as they should not need to know about
- the position or length of the gap.
- ******************************************************************************/
-
- long CPEditText::GetGapPosition()
- {
- return itsGapPosition;
- }
-
-
- /******************************************************************************
- GetGapLength
-
- Return the length of the insertion gap. Clients will generally
- not need to use this method, as they should not need to know about
- the position or length of the gap.
- ******************************************************************************/
-
- long CPEditText::GetGapLength()
- {
- return itsGapLength;
- }
-
- #endif // qPEUseInsertionGap
-
-
- /**** H O O K M E T H O D S ****/
-
-
- /******************************************************************************
- WordBreakHook
-
- Return the character offset at which the next word break in the
- specified direction should occur. Override if a different work
- break criterion is desired.
- ******************************************************************************/
-
- #define TestBit(p,b) (((long *)(p))[(b) >> 5] & (0x80000000U >> ((b) & 0x1F)))
-
- long CPEditText::WordBreakHook(register long charPos, BreakDirection direction)
- {
- register Byte c;
- register long textLength;
- register Ptr textP = *itsTextHandle;
- #if qPEUseInsertionGap
- register long gapLength = itsGapLength;
- long gapPosition = itsGapPosition;
- register Ptr gapP = textP + gapPosition;
- #endif
- static long wordBreaks[8] = { // Packed boolean flags to indicate
- 0x00000000, 0x0000FFC0, // whether or not a character is a
- 0x7FFFFFE1, 0x7FFFFFE0, // word break character
- 0x00000000, 0x00000000,
- 0x00000000, 0x00000000
- };
-
- textP += charPos;
- #if qPEUseInsertionGap
- if (charPos >= gapPosition)
- textP += gapLength;
- #endif
-
- if (direction == kBreakLeft) {
- #if qPEUseInsertionGap
- gapP += gapLength;
- #endif
-
- do {
- #if qPEUseInsertionGap
- if (textP == gapP)
- textP -= gapLength;
- #endif
- } while (charPos-- && (c = *--textP, TestBit(wordBreaks, c)));
-
- return charPos + 1;
- }
- else {
- textLength = itsTextLength;
-
- do {
- #if qPEUseInsertionGap
- if (textP == gapP)
- textP += gapLength;
- #endif
- } while (charPos < textLength && (c = *textP++, TestBit(wordBreaks, c)) && ++charPos);
-
- return charPos;
- }
- }
-
-
- /******************************************************************************
- CaretHook
-
- Draw the text insertion caret. The rectangle is in QuickDraw
- coordinates. Override if a different caret appearance is desired.
- ******************************************************************************/
-
- void CPEditText::CaretHook(const Rect *caretRect)
- {
- PenState prevPenState;
- Boolean canDrawInRealGray = FALSE;
- RGBColor grayColor;
- RGBColor prevForeColor;
-
- GetPenState(&prevPenState);
- PenMode(patXor);
-
- // Draw the caret in gray if the pane isn't active
- if (!fReallyActive && fOutlineHilite) {
- canDrawInRealGray = GetGrayRGBColor(caretRect, &grayColor, &prevForeColor);
- if (canDrawInRealGray)
- RGBForeColor(&grayColor);
- else
- PenPat(gray);
- }
-
- // Draw the caret
- if ((itsTextFace & italic) && fUseItalicCaret) {
- PenSize(1, 1);
- MoveTo(caretRect->left, caretRect->bottom);
- LineTo(caretRect->left + (caretRect->bottom - caretRect->top) / 2, caretRect->top);
- }
- else
- PaintRect(caretRect);
-
- SetPenState(&prevPenState);
- if (canDrawInRealGray)
- RGBForeColor(&prevForeColor);
- }
-
-
- /******************************************************************************
- HiliteHook
-
- Hilite the specified rectangle. The rectangle is in QuickDraw
- coordinates. Override if a different hiliting behavior is desired.
- ******************************************************************************/
-
- void CPEditText::HiliteHook(const Rect *hiliteRect)
- {
- if ((itsTextFace & italic) && fUseItalicCaret) {
- OpenRgn();
- MoveTo(hiliteRect->left, hiliteRect->bottom);
- LineTo(hiliteRect->right, hiliteRect->bottom);
- LineTo(hiliteRect->right + (hiliteRect->bottom - hiliteRect->top) / 2, hiliteRect->top);
- LineTo(hiliteRect->left + (hiliteRect->bottom - hiliteRect->top) / 2, hiliteRect->top);
- LineTo(hiliteRect->left, hiliteRect->bottom);
- CloseRgn(gUtilRgn);
- SetHiliteMode();
- InvertRgn(gUtilRgn);
- }
- else {
- SetHiliteMode();
- InvertRect(hiliteRect);
- }
- }
-
-
- /**** I N T E R N A L M E T H O D S ****/
-
-
- #if qPEUseInsertionGap
-
- /******************************************************************************
- SetGapPosition
-
- Internal method to set the position of the insertion gap.
- ******************************************************************************/
-
- void CPEditText::SetGapPosition(long newGapPosition)
- {
- long gapPosition = itsGapPosition;
-
- if ((newGapPosition != gapPosition) && (newGapPosition >= 0) && (newGapPosition <= itsTextLength)) {
- Ptr textP = *itsTextHandle;
- long textLength = itsTextLength;
- long gapLength = itsGapLength;
-
- if (gapLength > 0) {
- if (newGapPosition > gapPosition)
- BlockMove(textP + gapPosition + gapLength, textP + gapPosition, newGapPosition - gapPosition);
- else
- BlockMove(textP + newGapPosition, textP + newGapPosition + gapLength, gapPosition - newGapPosition);
- }
-
- itsGapPosition = newGapPosition;
- }
- }
-
-
- /******************************************************************************
- SetGapLength
-
- Internal method to set the length of the insertion gap.
- ******************************************************************************/
-
- void CPEditText::SetGapLength(long newGapLength)
- {
- if ((newGapLength != itsGapLength) && (newGapLength >= 0)) {
- long gapLength = itsGapLength;
- long gapPosition = itsGapPosition;
- long textLength = itsTextLength;
- Handle textHandle = itsTextHandle;
-
- if (newGapLength < gapLength) {
- BlockMove(*textHandle + gapPosition + gapLength,
- *textHandle + gapPosition + newGapLength,
- textLength - gapPosition);
- SetHandleSize(textHandle, textLength + newGapLength);
- itsGapLength = newGapLength;
- }
- else {
- ResizeHandleCanFail(textHandle, textLength + newGapLength);
- if (MemError() == noErr) {
- BlockMove(*textHandle + gapPosition + gapLength,
- *textHandle + gapPosition + newGapLength,
- textLength - gapPosition);
- itsGapLength = newGapLength;
- }
- }
-
- }
- }
-
-
- /******************************************************************************
- CloseGap
-
- Internal method to close the insertion gap.
- ******************************************************************************/
-
- void CPEditText::CloseGap()
- {
- SetGapLength(0);
- SetGapPosition(0);
- }
-
- #endif // qPEUseInsertionGap
-
-
- /******************************************************************************
- CheckInsertion
-
- Internal method to "preflight" an attempt to insert text and ensure
- that enough memory is available for the insertion to succeed.
-
- If useSelection is TRUE, the length of the selection is deducted
- from the total length. If numInsertCRs is non-NULL, the number of
- return characters in the text being inserted is returned. If
- useSelection is TRUE and numSelectionCRs is non-NULL, the number
- of return characters in the current selection is returned.
- ******************************************************************************/
-
- void CPEditText::CheckInsertion(
- Ptr insertPtr, // Ptr to text being inserted
- long insertLen, // Length of text being inserted
- Boolean useSelection, // Compensate for selection length?
- long *numInsertCRs, // Number of CRs in inserted text
- long *numSelectionCRs) // Number of CRs in selection
- {
- long prevTextLen; // Previous length of text handle
- long prevLineStartsLen; // Previous length of line starts
- long newTextLen; // New length of text handle
- long newLineStartsLen; // New length of line starts
- long insertCRsCount; // Number of CRs in inserted text
-
- // Save the current lengths of the text and line starts handles
- prevTextLen = GetHandleSize(itsTextHandle);
- prevLineStartsLen = GetHandleSize((Handle)itsLineStarts);
-
- // Count the number of return characters in the text being inserted
- if ((insertPtr != NULL) && (insertLen > 0))
- insertCRsCount = CountCRs(insertPtr, insertLen);
- else
- insertCRsCount = 0;
-
- // Compute the new lengths of the handles
- newTextLen = prevTextLen + insertLen;
- newLineStartsLen = prevLineStartsLen + insertCRsCount * sizeof(long);
- if (numInsertCRs != NULL)
- *numInsertCRs = insertCRsCount;
-
- // If we're taking the selection into account, adjust the new lengths
- // of the handles accordingly
- if (useSelection) {
- long crCount = CountRangeCRs(itsSelStart, itsSelEnd);
-
- newTextLen -= (itsSelEnd - itsSelStart);
- newLineStartsLen -= crCount * sizeof(long);
-
- if (numSelectionCRs != NULL)
- *numSelectionCRs = crCount;
- }
-
- // Attempt to resize the handles
- TRY {
- if (newTextLen > prevTextLen) {
- ResizeHandleCanFail(itsTextHandle, newTextLen);
- FailMemError();
- }
-
- if (newLineStartsLen > prevLineStartsLen) {
- ResizeHandleCanFail((Handle)itsLineStarts, newLineStartsLen);
- FailMemError();
- SetHandleSize((Handle)itsLineStarts, prevLineStartsLen);
- }
-
- if (newTextLen > prevTextLen)
- SetHandleSize(itsTextHandle, prevTextLen);
- }
- CATCH {
- SetHandleSize(itsTextHandle, prevTextLen);
- SetHandleSize((Handle)itsLineStarts, prevLineStartsLen);
- }
- ENDTRY
- }
-
-
- /******************************************************************************
- CountRangeCRs
-
- Internal method to count the number of carriage return characters
- in the given range of text.
- ******************************************************************************/
-
- long CPEditText::CountRangeCRs(long start, long end)
- {
- Ptr textP = *itsTextHandle;
- long length = end - start;
- #if qPEUseInsertionGap
- long gapPosition = itsGapPosition;
- long gapLength = itsGapLength;
- #endif
- long crCount;
-
- if (start >= end)
- crCount = 0;
- else if ((start == 0) && (end == itsTextLength))
- crCount = itsNumLines - 1;
- #if qPEUseInsertionGap
- else if (end <= gapPosition)
- crCount = CountCRs(textP + start, length);
- else if (start >= gapPosition)
- crCount = CountCRs(textP + start + gapLength, length);
- else {
- long lenBeforeGap = gapPosition - start;
- crCount = CountCRs(textP + start, lenBeforeGap);
- crCount += CountCRs(textP + start + gapLength + lenBeforeGap, length - lenBeforeGap);
- }
- #else
- else
- crCount = CountCRs(textP + start, length);
- #endif
-
- return crCount;
- }
-
-
- /******************************************************************************
- InsertText
-
- Internal method to insert a copy of the given text at the start of
- the selection. Called by InsertTextPtr and ReplaceSelection.
- Assumes that CheckInsertion has been called to assure that the
- insertion will succeed.
- ******************************************************************************/
-
- void CPEditText::InsertText(
- Ptr insertPtr, // Pointer to text to insert
- long insertLen, // Length of text to insert
- long numInsertCRs, // Number of CRs in inserted text
- Boolean redraw) // TRUE to redraw text
- {
- Ptr textP;
- long selStart = itsSelStart;
- long selEnd = itsSelEnd;
-
- HideSelection(kHideSelection, kRedraw);
-
- #if qPEUseInsertionGap
- // Check if the text being inserted will fit into the gap
- // If not, resize the text handle to accomodate the new text
- if ((selStart == itsGapPosition) && (insertLen <= itsGapLength)) {
- textP = *itsTextHandle;
- if (insertLen == 1) // Special case to avoid BlockMove overhead
- *(textP + selStart) = *insertPtr;
- else
- BlockMove(insertPtr, textP + selStart, insertLen);
-
- itsGapPosition += insertLen;
- itsGapLength -= insertLen;
- }
- else {
- CloseGap();
- #endif
- SetHandleSize(itsTextHandle, itsTextLength + insertLen);
- FailMemError();
- textP = *itsTextHandle;
- BlockMove(textP + selStart, textP + selStart + insertLen, itsTextLength - selStart);
- BlockMove(insertPtr, textP + selStart, insertLen);
- #if qPEUseInsertionGap
- }
- #endif
-
- itsTextLength += insertLen;
-
- // Adjust the line starts and redraw the new text if necessary
- AdjustLineStarts(selStart, insertLen, numInsertCRs);
- if (!redraw)
- RefreshTextAfter(selStart, (numInsertCRs == 0));
-
- // Set the selection to the end of the inserted text
- SetSelection(selStart + insertLen, selEnd + insertLen, kNoRedraw);
-
- // Redraw the entire pane if requested
- if (redraw)
- Refresh();
- }
-
-
- /******************************************************************************
- DeleteText
-
- Internal method to delete a range of characters. Called by
- DeleteTextRange and ReplaceSelection.
- ******************************************************************************/
-
- void CPEditText::DeleteText(
- long start,
- long end,
- long numDeleteCRs,
- Boolean redraw)
- {
- long length;
- long selStart;
- long selEnd;
-
- length = end - start;
- if (length > 0) {
-
- #if qPEUseInsertionGap
- // Check if we can delete the selected text by simply adding it to the insertion gap
- if ((itsGapLength == 0) || (start == itsGapPosition) || (end == itsGapPosition)) {
- itsGapPosition = start;
- itsGapLength += length;
- itsTextLength -= length;
- }
- else {
- // Close up the insertion gap and remove the selected text from the text handle
- CloseGap();
- #endif
- BlockMove(*itsTextHandle + end, *itsTextHandle + start, itsTextLength - end);
- itsTextLength -= length;
- SetHandleSize(itsTextHandle, itsTextLength);
- #if qPEUseInsertionGap
- }
- #endif
-
- // Adjust the line starts array
- AdjustLineStarts(start, -length, -numDeleteCRs);
-
- // Adjust the current selection
- GetSelection(&selStart, &selEnd);
- if (selStart >= start) {
- if (selStart >= end)
- selStart -= length;
- else
- selStart = start;
- }
- if (selEnd >= start) {
- if (selEnd >= end)
- selEnd -= length;
- else
- selEnd = selStart;
- }
- SetSelection(selStart, selEnd, kNoRedraw);
-
- // Refresh the necessary text if requested
- if (redraw)
- RefreshTextAfter(start, (numDeleteCRs == 0));
- }
- }
-
-
- /******************************************************************************
- RefreshTextAfter
-
- Internal method to redraw the text following the specified character.
- ******************************************************************************/
-
- void CPEditText::RefreshTextAfter(
- long afterPos,
- Boolean refreshOnlyLine)
- {
- long startLine;
- long endLine;
- long vertInset = VertInset();
- LongRect lr;
- Rect r;
-
- Prepare();
-
- // Redraw the text after the given offset on the line
- startLine = FindLine(afterPos);
- if (refreshOnlyLine)
- endLine = startLine;
- else {
- endLine = (frame.bottom - vertInset) / itsLineHeight;
- endLine = Min(endLine, itsNumLines - 1);
- }
-
- DrawLineRange(startLine, endLine, afterPos - GetLineStart(startLine), kEraseText);
-
- // Erase the area below the last text line, if necessary
- if (!refreshOnlyLine) {
- SetLongRect(&lr, frame.left, (endLine + 1) * itsLineHeight + vertInset, frame.right, frame.bottom);
- if (lr.bottom > lr.top) {
- FrameToQDR(&lr, &r);
- EraseRect(&r);
- }
- }
- }
-
-
- /******************************************************************************
- DrawLineRange
-
- Internal method to draw the specified range of text lines. Assumes
- the pane has been prepared.
- ******************************************************************************/
-
- void CPEditText::DrawLineRange(
- long startLine,
- long endLine,
- long startLineOffset,
- Boolean erase)
- {
- Boolean showInvisibles = fShowInvisibles;
- char textHState;
- register char tab;
- Handle invisH;
- long line;
- long lineStart;
- long lineEnd;
- long firstChar;
- long boundsHExtent;
- long horizInset = HorizInset();
- #if qPEUseInsertionGap
- long gapPosition = itsGapPosition;
- long gapLength = itsGapLength;
- #endif
- LongPt textPt;
- LongRect lr;
- Point penPt;
- Point startPt;
- #if qPEUseInsertionGap
- Ptr gapP;
- #endif
- Rect eraseRect;
- RgnHandle clipRgn;
- register Ptr textP;
- register short numChars;
- register short index;
- register short tabWidth = itsTabWidth;
- ShortHandle widthsH = NULL;
- short width;
- short *widthsP;
- short fontAscent = itsFontAscent;
- short lineHeight = itsLineHeight;
-
- // Lock down the text handle
- textHState = HGetState(itsTextHandle);
- HLock(itsTextHandle);
-
- // If we are erasing, save the current clipping region
- if (erase) {
- GetClip(cSaveClipRgn);
- clipRgn = NewRgn();
- }
-
- // Compute the vertical coordinate for the first line of text
- textPt.v = startLine * lineHeight + fontAscent + VertInset();
-
- // Draw the specified lines of text
- for (line = startLine; line <= endLine; ++line) {
-
- // Compute the number of characters to draw
- lineStart = GetLineStart(line);
- lineEnd = GetLineEnd(line);
-
- firstChar = lineStart + startLineOffset;
- numChars = lineEnd - firstChar;
-
- if ((line == startLine) && (startLineOffset > 0)) {
- widthsH = MeasureLineWidths(line);
- textPt.h = (*widthsH)[startLineOffset] + horizInset;
- }
- else {
- widthsH = NULL;
- textPt.h = horizInset;
- }
-
- FrameToQD(&textPt, &startPt);
-
- // Erase the background behind the text, if desired
- if (erase) {
- SetLongRect(&lr, (line == startLine && startLineOffset > 0 ? textPt.h : frame.left),
- textPt.v - fontAscent, frame.right, textPt.v - fontAscent + lineHeight);
- FrameToQDR(&lr, &eraseRect);
-
- // Adjust the clipping region and set things so that the entire line
- // is redrawn -- this is so that italic characters don't get clipped
- RectRgn(clipRgn, &eraseRect);
- SectRgn(clipRgn, cSaveClipRgn, clipRgn);
- SetClip(clipRgn);
-
- firstChar = lineStart;
- numChars = lineEnd - lineStart;
-
- textPt.h = horizInset;
- FrameToQD(&textPt, &startPt);
- }
-
- ForgetHandle(widthsH);
-
- // If invisible characters are visible, make a copy of the text
- // and convert all the invisible characters
- if (showInvisibles) {
- invisH = CopyTextRange(lineStart, lineEnd);
- numChars = ConvertInvisibles(invisH, numChars);
- HLock(invisH);
- textP = *invisH;
- #if qPEUseInsertionGap
- gapP = NULL;
- gapPosition = gapLength = 0;
- #endif
- }
- else {
- #if qPEUseInsertionGap
- textP = *itsTextHandle;
- gapP = textP + gapPosition;
- textP += firstChar;
- if (firstChar > gapPosition)
- textP += gapLength;
- #else
- textP = *itsTextHandle + firstChar;
- #endif
- }
-
- // Now draw the line of text
-
- FrameToQD(&textPt, &penPt);
- MoveTo(penPt.h, penPt.v);
- if (erase)
- EraseRect(&eraseRect);
-
- #if THINK_C
- asm {
- clr.w index ; reset index
- move.b #kTab,tab ; keep tab character in register for speed
-
- @Loop:
- cmp.w numChars,index ; have we drawn all the text?
- bge.s @Done ; branch if we have
- #if qPEUseInsertionGap
- cmpa.l gapP,textP ; are we at the gap?
- bne.s @NotAtGap ; branch if not
-
- @AtGap:
- bsr.s @DrawText ; else draw the text to the left of the gap
- adda.l gapLength,textP ; and skip over the gap
-
- @NotAtGap:
- #endif
- addq.w #1,index ; else bump the index
- cmp.b (textP)+,tab ; is character from text a tab?
- bne.s @Loop ; loop if not
-
- @IsTab:
- subq.w #1,textP ; decrement ptr to text
- subq.w #1,index ; decrement # of characters to draw
- bsr.s @DrawText ; draw the text to the left of the tab
-
- movea.l thePort,a0 ; get current GrafPtr
- move.l OFFSET(GrafPort,pnLoc)(a0),penPt ; get pen location
-
- tst.b showInvisibles ; are we displaying invisible characters?
- beq.s @NoInvis1 ; branch if not
- move.w #kInvisTab,-(a7) ; else push invisible tab character
- _DrawChar ; and draw it
-
- @NoInvis1:
- moveq #0,d0 ; clear out all of D0
- move.w penPt.h,d0 ; get horiz pen location
- sub.w startPt.h,d0 ; subtract starting location
- divu.w tabWidth,d0 ; divide by tab width
- addq.w #1,d0 ; add one to tab width
- mulu.w tabWidth,d0 ; get pixel offset
- add.w startPt.h,d0 ; add starting location
-
- move.w d0,-(a7) ; push horiz coordinate
- move.w penPt.v,-(a7) ; push vert coordinate
- _MoveTo ; move pen location
-
- addq.w #1,textP ; bump ptr to text
- subq.w #1,numChars ; subtract 1 from length of text
- bra.s @Loop ; and loop
-
- @DrawText:
- tst.w index ; any text to draw?
- beq.s @NoText ; skip if not
-
- movea.l textP,a0 ; get ptr to next character
- suba.w index,a0 ; subtract index
- move.l a0,-(a7) ; push ptr to text
- clr.w -(a7) ; push offset into text
- move.w index,-(a7) ; push length of text to draw
- _DrawText ; call DrawText
-
- sub.w index,numChars ; subtract from length of text
- clr.w index ; reset index to 0
- @NoText:
- rts
-
- @Done:
- bsr.s @DrawText ; draw rest of text
- }
- #else
- #if qPEUseInsertionGap
- AsmDrawLineRange(textP, numChars, tabWidth, gapP, gapLength, startPt, showInvisibles);
- #else
- AsmDrawLineRange(textP, numChars, tabWidth, NULL, 0, startPt, showInvisibles);
- #endif
- #endif
-
- // Dispose the temporary handle for invisible characters
- if (showInvisibles)
- DisposHandle(invisH);
-
- // Increment the text vertical coordinate
- textPt.v += lineHeight;
- }
-
- // Restore the previous clipping region and the previous state of the text handle
- if (erase) {
- SetClip(cSaveClipRgn);
- DisposeRgn(clipRgn);
- }
-
- HSetState(itsTextHandle, textHState);
- }
-
-
- /******************************************************************************
- HiliteTextRange
-
- Internal method to highlight the given range of text. Assumes
- the pane has been prepared.
- ******************************************************************************/
-
- void CPEditText::HiliteTextRange(long start, long end)
- {
- Boolean isActive = fReallyActive;
- long startLine;
- long endLine;
- LongPt startPt;
- LongPt endPt;
- LongRect hiliteRect;
- PenState penState;
- Rect qdRect;
- RgnHandle hiliteRgn;
- RgnHandle rectRgn;
- short hSpan;
- short vSpan;
- short fontAscent = itsFontAscent;
- short lineHeight = itsLineHeight;
-
- if (isActive || fOutlineHilite) {
-
- // Outline the selection range if the pane is not active
- if (!isActive) {
- hiliteRgn = NewRgn();
- rectRgn = NewRgn();
- }
-
- // Get the starting and ending selection lines and the number of lines
- // spanned by the frame
- startLine = FindLine(start);
- endLine = FindLine(end);
- GetFrameSpan(&hSpan, &vSpan);
-
- // Take a quick exit if the selection is not visible within the frame span
- if ((startLine >= position.v + vSpan) || (endLine < position.v))
- return;
-
- // Adjust the starting and ending selection lines if they are outside the frame
- if (startLine < position.v) {
- startLine = position.v;
- start = GetLineStart(startLine);
- }
-
- if (endLine > position.v + vSpan) {
- endLine = position.v + vSpan;
- end = GetLineEnd(endLine);
- }
-
- // Get the frame coordinates corresponding to the
- // start and end of the selection range
- GetCharPoint(start, &startPt);
- GetCharPoint(end, &endPt);
-
- // Adjust the horizontal coordinates if the either of the starting
- // or ending characters lies at the beginning of a line
- if (start == GetLineStart(startLine))
- startPt.h = frame.left;
- if (end == GetLineStart(endLine))
- endPt.h = frame.left;
-
- // Check for and handle a multiple-line selection range
- if (startPt.v != endPt.v) {
-
- // Highlight the first line of the selection range
- SetLongRect(&hiliteRect, startPt.h, startPt.v - fontAscent, frame.right, startPt.v - fontAscent + lineHeight);
- FrameToQDR(&hiliteRect, &qdRect);
-
- if (isActive)
- HiliteHook(&qdRect);
- else {
- qdRect.left -= 1;
- RectRgn(rectRgn, &qdRect);
- UnionRgn(rectRgn, hiliteRgn, hiliteRgn);
- }
-
- // Highlight the middle lines of the selection range
- hiliteRect.left = frame.left;
-
- if (isActive) {
- FrameToQDR(&hiliteRect, &qdRect);
-
- while (++startLine < endLine) {
- qdRect.top += lineHeight;
- qdRect.bottom += lineHeight;
- HiliteHook(&qdRect);
- }
- }
- else {
- hiliteRect.top += lineHeight;
- hiliteRect.left -= 1;
- hiliteRect.bottom = endPt.v - fontAscent + 1;
- FrameToQDR(&hiliteRect, &qdRect);
- RectRgn(rectRgn, &qdRect);
- UnionRgn(rectRgn, hiliteRgn, hiliteRgn);
- }
-
- SetLongPt(&startPt, hiliteRect.left, endPt.v);
- }
-
- // Hilite the last part of the selection range
- SetLongRect(&hiliteRect, startPt.h, endPt.v - fontAscent, endPt.h, endPt.v - fontAscent + lineHeight);
- FrameToQDR(&hiliteRect, &qdRect);
-
- if (isActive)
- HiliteHook(&qdRect);
- else {
- if (qdRect.right > qdRect.left + 1) {
- qdRect.bottom += 1;
- RectRgn(rectRgn, &qdRect);
- UnionRgn(rectRgn, hiliteRgn, hiliteRgn);
- }
-
- // Intersect the highlight region with the pane frame
- FrameToQDR(&frame, &qdRect);
- RectRgn(rectRgn, &qdRect);
- SectRgn(rectRgn, hiliteRgn, hiliteRgn);
- DisposeRgn(rectRgn);
-
- // Outline the highlight region
- GetPenState(&penState);
- PenNormal();
- PenMode(patXor);
- FrameRgn(hiliteRgn);
- DisposeRgn(hiliteRgn);
- SetPenState(&penState);
- }
- }
- }
-
-
- /******************************************************************************
- GetTextWidth
-
- Internal method to return the number of pixels between the
- specified characters on a given line.
- ******************************************************************************/
-
- short CPEditText::GetTextWidth(long line, short startPos, short endPos)
- {
- ShortHandle widthsH;
- short width;
-
- if (endPos > startPos) {
- widthsH = MeasureLineWidths(line);
- width = (*widthsH)[endPos] - (*widthsH)[startPos];
- DisposHandle((Handle)widthsH);
- }
- else
- width = 0;
-
- return width;
- }
-
-
- /******************************************************************************
- MeasureLineWidths
-
- Internal method to compute and return an array of pixel distances
- for the given line.
- ******************************************************************************/
-
- ShortHandle CPEditText::MeasureLineWidths(long line)
- {
- return MeasureTextWidths(GetLineStart(line), GetLineEnd(line), MAXINT);
- }
-
-
- /******************************************************************************
- MeasureTextWidths
-
- Internal method to compute and return an array of pixel distances
- for the text between the given offsets. The maxWidth parameter
- specifies the maximum pixel width that the client is interested in.
- ******************************************************************************/
-
- ShortHandle CPEditText::MeasureTextWidths(long startPos, long endPos, short maxWidth)
- {
- char textHState;
- #if THINK_C
- register char tab;
- #endif
- Handle invisH = NULL;
- #if qPEUseInsertionGap
- long gapPosition = itsGapPosition;
- long gapLength = itsGapLength;
- Ptr gapP;
- #endif
- register Ptr textP;
- register short numChars;
- #if THINK_C
- register short index;
- short lastWidth;
- #endif
- register short tabWidth = itsTabWidth;
- register short *widthsP;
- ShortHandle widthsH;
-
- // Lock down the text handle and allocate space for the widths array
- textHState = HGetState(itsTextHandle);
- HLock(itsTextHandle);
-
- numChars = (short)(endPos - startPos);
- widthsH = (ShortHandle)NewHandle((numChars + 1) * sizeof(short));
- HLock((Handle)widthsH);
- widthsP = *widthsH;
-
- // If "show invisible characters" is enabled, make a copy of the text
- // and convert all the invisible characters
- if (fShowInvisibles) {
- invisH = CopyTextRange(startPos, endPos);
- numChars = ConvertInvisibles(invisH, numChars);
- HLock(invisH);
- textP = *invisH;
- #if qPEUseInsertionGap
- gapP = NULL;
- gapPosition = gapLength = 0;
- #endif
- }
- else {
- #if qPEUseInsertionGap
- textP = *itsTextHandle;
- gapP = textP + gapPosition;
- textP += startPos;
- if (startPos >= gapPosition)
- textP += gapLength;
- #else
- textP = *itsTextHandle + startPos;
- #endif
- }
-
- // Measure the text and save the pixel widths
- Prepare();
-
- #if THINK_C
- asm {
- clr.w index ; reset index
- clr.w lastWidth ; set lastWidth to zero
- move.b #kTab,tab ; keep tab character in register for speed
-
- @Loop:
- cmp.w numChars,index ; have we measured all the text?
- bge.s @Done ; branch if we have
- #if qPEUseInsertionGap
- cmpa.l gapP,textP ; are we at the gap?
- bne.s @NotAtGap ; branch if not
-
- @AtGap:
- bsr.s @MeasTxt ; else measure the text to the left of the gap
- adda.l gapLength,textP ; and skip over the gap
-
- @NotAtGap:
- #endif
- addq.w #1,index ; else bump the index
- cmp.b (textP)+,tab ; is character from text a tab?
- bne.s @Loop ; loop if not
-
- @IsTab:
- subq.w #1,textP ; decrement ptr to text
- subq.w #1,index ; decrement # of characters to measure
- bsr.s @MeasTxt ; measure the text to the left
-
- moveq #0,d0 ; clear out all of d0
- move.w lastWidth,d0 ; get total width of text so far
- divu.w tabWidth,d0 ; divide by tab width
- addq.w #1,d0 ; add one to tab width
- mulu.w tabWidth,d0 ; compute width at next tab stop
- addq.w #2,widthsP ; bump widthsP
- move.w d0,(widthsP) ; save width of tab in widths array
- move.w d0,lastWidth ; remember new total width
-
- addq.w #1,textP ; bump ptr to text
- subq.w #1,numChars ; decrement length of text
- bra.s @Loop ; and loop
-
- @MeasTxt:
- move.w index,-(a7) ; push number of characters to measure
- movea.l textP,a0 ; get ptr to next character in text
- suba.w index,a0 ; subtract index
- move.l a0,-(a7) ; push ptr to text to measure
- move.l widthsP,-(a7) ; push ptr to current position in widths array
- _MeasureText ; measure the text
-
- move.w lastWidth,d0 ; keep lastWidth in d0
- move.w index,d1 ; loop index in d1
- @WidthLoop:
- add.w d0,(widthsP)+ ; add lastWidth to element of widths array
- dbra d1,@WidthLoop ; and loop
-
- move.w -(widthsP),lastWidth ; save width of last character
- sub.w index,numChars ; decrement length of text
- clr.w index ; reset character count
- rts ; and return to caller
-
- @Done:
- bsr.s @MeasTxt ; measure the rest of the text
- }
- #else
- #if qPEUseInsertionGap
- AsmMeasureTextWidths(textP, numChars, tabWidth, gapP, gapLength, widthsP, maxWidth);
- #else
- AsmMeasureTextWidths(textP, numChars, tabWidth, NULL, 0, widthsP, maxWidth);
- #endif
- #endif
-
- // Clean up
- HUnlock((Handle)widthsH);
- HSetState(itsTextHandle, textHState);
- ForgetHandle(invisH);
-
- return widthsH;
- }
-
-
- /******************************************************************************
- ConvertInvisibles
-
- Internal method to convert any invisible characters in the given
- text to their corresponding visible characters. The handle may
- be resized if necessary, so the method returns the number of
- characters in the resized handle.
- ******************************************************************************/
-
- short CPEditText::ConvertInvisibles(Handle invisH, short numChars)
- {
- register Ptr invisP = *invisH;
- register short count = numChars;
-
- while (--count >= 0) {
- if (*invisP <= kSpace) {
- switch (*invisP) {
- case kSpace:
- *invisP = kInvisSpace;
- break;
- case kReturn:
- *invisP = kInvisReturn;
- break;
- case kLineFeed:
- *invisP = kInvisLineFeed;
- break;
- case kFormFeed:
- *invisP = kInvisFormFeed;
- break;
- case kTab:
- // Don't convert tabs here
- break;
- default:
- *invisP = kInvisOther;
- break;
- }
- }
-
- ++invisP;
- }
-
- return numChars; // since we don't yet resize the handle
- }
-
-
- /******************************************************************************
- AdjustBounds
-
- Internal method to adjust the bounds of a PEditText panorama.
- Performed whenever something happens which can affect the number of
- lines of text or the line width.
- ******************************************************************************/
-
- void CPEditText::AdjustBounds()
- {
- LongRect newBounds;
-
- newBounds.top = 0;
- newBounds.left = 0;
- newBounds.bottom = itsNumLines;
- newBounds.right = (kDefaultBoundsWidth - 1) / hScale + 1;
- SetBounds(&newBounds);
- }
-
-
- /******************************************************************************
- CalcLineHeight
-
- Internal method to calculate the line height and font ascent.
- Called by SetFontNumber, SetFontSize, SetFontStyle and SetSpacingCmd.
- ******************************************************************************/
-
- void CPEditText::CalcLineHeight()
- {
- FontInfo macFontInfo;
-
- // Update itsLineHeight, itsFontAscent and itsMaxCharWidth
- GetMacFontInfo(&macFontInfo);
- itsLineHeight = macFontInfo.ascent + macFontInfo.descent + macFontInfo.leading;
- itsFontAscent = macFontInfo.ascent;
- itsMaxCharWidth = macFontInfo.widMax;
-
- if (itsSpacingCmd == cmd1HalfSpace) {
- itsLineHeight *= 3;
- itsLineHeight /= 2;
- }
- else if (itsSpacingCmd == cmdDoubleSpace)
- itsLineHeight *= 2;
-
- CalcTabWidth();
-
- // Refresh the contents of the pane
- Refresh();
- SetScales(itsMaxCharWidth, itsLineHeight);
- AdjustBounds();
- SetWholeLines(wholeLines);
- Refresh();
- }
-
-
- /******************************************************************************
- CalcTabWidth
-
- Internal method to calculate the width of a tab. Called by
- SetTabSpaces and CalcLineHeight.
- ******************************************************************************/
-
- void CPEditText::CalcTabWidth()
- {
- Prepare();
- itsTabWidth = itsTabSpaces * CharWidth(kSpace);
- }
-
-
- /******************************************************************************
- CalcLineStarts
-
- Internal method to calculate the line starts array.
- ******************************************************************************/
-
- void CPEditText::CalcLineStarts()
- {
- Boolean origAlloc;
- register Byte cr = kReturn;
- char textHState;
- register long charPos;
- register long textLength = itsTextLength;
- register long *lineStarts;
- register long numLines;
- #if qPEUseInsertionGap
- long gapLength = itsGapLength;
- long gapPosition = itsGapPosition;
- register Ptr gapP;
- #endif
- register Ptr textP;
- unsigned long startTicks = TickCount();
-
- // Initialize our pointer to the text
- textHState = HGetState(itsTextHandle);
- HLock(itsTextHandle);
- textP = *itsTextHandle;
- #if qPEUseInsertionGap
- gapP = textP + gapPosition;
- #endif
-
- // Count the number of lines in the text
- charPos = itsTextLength;
- numLines = 1;
-
- while (--charPos >= 0) {
- #if qPEUseInsertionGap
- if (textP == gapP)
- textP += gapLength;
- #endif
- if (*textP++ == cr)
- ++numLines;
- }
-
- // Reallocate the line starts array
-
- // Here we intentionally make resizing the line starts handle
- // a critical operation so that it is less likely to fail.
- // The rationale is that resizing the text handle is far more
- // likely to exceed available memory than resizing the line
- // starts handle (since the line starts handle is normally much
- // smaller), and falling back at this point is somewhat tricky
- // (although by no means impossible.)
-
- origAlloc = SetAllocation(kAllocCantFail);
- SetHandleSize((Handle)itsLineStarts, numLines * sizeof(long));
- (void)SetAllocation(origAlloc);
- FailMemError();
-
- // Update the number of lines
- itsNumLines = numLines;
-
- // Build the line starts array
- lineStarts = *itsLineStarts + 1;
- textP = *itsTextHandle;
- charPos = 0;
-
- while (++charPos <= textLength) {
- #if qPEUseInsertionGap
- if (textP == gapP)
- textP += gapLength;
- #endif
- if (*textP++ == cr)
- *lineStarts++ = charPos;
- }
-
- HSetState(itsTextHandle, textHState);
-
- // Adjust the panorama bounds to match the number of lines in the text
- AdjustBounds();
- }
-
-
- /******************************************************************************
- AdjustLineStarts
-
- Internal method to increment the line starts by the given amount,
- starting at the given line.
- ******************************************************************************/
-
- void CPEditText::AdjustLineStarts(long startChar, register long numCharsDelta, long numLinesDelta)
- {
- register Byte cr = kReturn;
- register long *lineStarts;
- register long count;
- long startLine;
- register Ptr textP;
- #if qPEUseInsertionGap
- register Ptr gapP;
- #endif
-
- // Find the line corresponding to the starting character offset
- startLine = FindLine(startChar) + 1;
-
- // Adjust the line starts array depending on the change in the number of lines
- if (numLinesDelta == 0) {
-
- // Bump the values in the line starts array, starting at startLine
- lineStarts = *itsLineStarts + startLine;
- count = itsNumLines - startLine + 1;
- while (--count > 0)
- *lineStarts++ += numCharsDelta;
- }
- else if (numLinesDelta > 0) {
- Boolean origAlloc;
- #if qPEUseInsertionGap
- long gapPosition = itsGapPosition;
- long gapLength = itsGapLength;
- #endif
-
- // Insert new entries in the line starts array starting at startLine
- // (See comment in CalcLineStarts() method above for rationale here)
- origAlloc = SetAllocation(kAllocCantFail);
- SetHandleSize((Handle)itsLineStarts, (itsNumLines + numLinesDelta) * sizeof(long));
- (void)SetAllocation(origAlloc);
- FailMemError();
-
- if (startLine < itsNumLines)
- BlockMove(*itsLineStarts + startLine, *itsLineStarts + startLine + numLinesDelta, (itsNumLines - startLine) * sizeof(long));
- itsNumLines += numLinesDelta;
-
- // Calculate values for the new entries
- lineStarts = *itsLineStarts + startLine;
- textP = *itsTextHandle;
- #if qPEUseInsertionGap
- gapP = textP + gapPosition;
- #endif
- textP += startChar;
- #if qPEUseInsertionGap
- if (startChar >= gapPosition)
- textP += gapLength;
- #endif
-
- count = numCharsDelta;
- while (--count >= 0) {
- #if qPEUseInsertionGap
- if (textP == gapP)
- textP += gapLength;
- #endif
- if (*textP++ == cr)
- *lineStarts++ = startChar + (numCharsDelta - count);
- }
-
- // Adjust the values of the remaining entries
- count = itsNumLines - (startLine + numLinesDelta);
- while (--count >= 0)
- *lineStarts++ += numCharsDelta;
- }
- else { // numLinesDelta < 0
-
- // Delete entries from the line starts array starting at startLine
- itsNumLines += numLinesDelta;
- if (startLine < itsNumLines)
- BlockMove(*itsLineStarts + startLine + (-numLinesDelta), *itsLineStarts + startLine, (itsNumLines - startLine) * sizeof(long));
- SetHandleSize((Handle)itsLineStarts, itsNumLines * sizeof(long));
-
- // Bump the values in the remaining entries
- lineStarts = *itsLineStarts + startLine;
- count = itsNumLines - startLine + 1;
- while (--count > 0)
- *lineStarts++ += numCharsDelta;
- }
-
- // Adjust the bounds rectangle if the number of lines changed
- if (numLinesDelta != 0)
- AdjustBounds();
- }
-
-
- /******************************************************************************
- DrawCaret
-
- Internal method to draw the text insertion caret.
- ******************************************************************************/
-
- void CPEditText::DrawCaret()
- {
- LongPt caretPt;
- LongRect caretRect;
- Rect qdRect;
-
- if (itsSelStart == itsSelEnd) {
- GetCharPoint(itsSelStart, &caretPt);
- SetLongRect(&caretRect, caretPt.h - 1, caretPt.v - itsFontAscent, caretPt.h, caretPt.v - itsFontAscent + itsLineHeight);
- FrameToQDR(&caretRect, &qdRect);
- CaretHook(&qdRect);
- }
- }
-
-
- /******************************************************************************
- ShowCaret
-
- Internal method to show the text insertion caret if it is not visible.
- ******************************************************************************/
-
- void CPEditText::ShowCaret()
- {
- if (!fCaretVisible) {
- DrawCaret();
- fCaretVisible = TRUE;
- itsCaretTime = TickCount() + GetCaretTime();
- }
- }
-
-
- /******************************************************************************
- HideCaret
-
- Internal method to hide the text insertion caret if it is visible.
- ******************************************************************************/
-
- void CPEditText::HideCaret()
- {
- if (fCaretVisible) {
- DrawCaret();
- fCaretVisible = FALSE;
- itsCaretTime = TickCount() + GetCaretTime();
- }
- }
-
-
- /******************************************************************************
- GetMacFontInfo
-
- Return a QuickDraw FontInfo record for the font used by a PEditText
- pane. This method changes the font family, style, and size of the
- pane's port so the Toolbox trap GetFontInfo can be used.
- ******************************************************************************/
-
- void CPEditText::GetMacFontInfo(FontInfo *macFontInfo)
- {
- ForceNextPrepare();
-
- SetPort(macPort);
- TextFont(itsTextFont);
- TextFace(itsTextFace);
- TextSize(itsTextSize);
- GetFontInfo(macFontInfo);
- }
-
-
- /**** U T I L I T Y F U N C T I O N S ****/
-
-
- /*============================================================================
- CountCRs
-
- Return the number of carriage return characters in the given text.
- =============================================================================*/
-
- static long CountCRs(register Ptr textP, register long numChars)
- {
- register Byte cr = kReturn;
- register long numCRs = 0;
-
- while (numChars--) {
- if (*textP++ == cr)
- ++numCRs;
- }
-
- return numCRs;
- }
-
-
- /*============================================================================
- GetGrayRGBColor
-
- Get the grayishTextOr gray RGB color. Return TRUE if the color
- was retrieved, FALSE otherwise.
- =============================================================================*/
-
- static Boolean GetGrayRGBColor(const Rect *localRect, RGBColor *grayColor, RGBColor *prevForeColor)
- {
- GDHandle targetDevice;
- Rect globalRect;
- RGBColor backColor;
-
- if (gSystem.hasColorQD && IsColorPort(thePort) && GestaltHasAttr(gestaltQuickdrawFeatures, gestaltHasGrayishTextOr)) {
- GetForeColor(prevForeColor);
- GetBackColor(&backColor);
- globalRect = *localRect;
- LocalToGlobal(&topLeft(globalRect));
- LocalToGlobal(&botRight(globalRect));
- targetDevice = GetMaxDevice(&globalRect);
- *grayColor = *prevForeColor;
-
- return GetGray(targetDevice, &backColor, grayColor);
- }
- else
- return FALSE;
- }
-
-
- /*============================================================================
- GestaltHasAttr
-
- Call Gestalt with the given selector and test the given bit in
- the response.
- =============================================================================*/
-
- static Boolean GestaltHasAttr(OSType selector, short responseBit)
- {
- long response;
-
- return (Gestalt(selector, &response) == noErr && (response & (1L << responseBit)) != 0);
- }
-